diff options
Diffstat (limited to 'v4l2-apps')
56 files changed, 3490 insertions, 1746 deletions
diff --git a/v4l2-apps/Make.rules b/v4l2-apps/Make.rules index d1b013317..5e471abcd 100644 --- a/v4l2-apps/Make.rules +++ b/v4l2-apps/Make.rules @@ -35,7 +35,7 @@ else @$(CC) -MMD $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) $(filter %.o %.c,$^) $(LOADLIBES) $(LDLIBS) -o $@ %.so: @echo LD $@ - @$(LD) -shared -o $@ $^ + @$(CC) -shared -o $@ $^ %.a: @echo AR $@ @$(AR) rcs $@ $^ diff --git a/v4l2-apps/Makefile b/v4l2-apps/Makefile index 550473435..2bba8fcd4 100644 --- a/v4l2-apps/Makefile +++ b/v4l2-apps/Makefile @@ -1,11 +1,24 @@ # Makefile for linuxtv.org v4l2-apps -.PHONY: all clean install +.PHONY: all distclean clean install -all clean install: +all:: prepare-includes + +all clean install:: $(MAKE) -C lib $@ $(MAKE) -C util $@ $(MAKE) -C test $@ %: make -C .. $(MAKECMDGOALS) + +clean:: + -$(RM) -rf include + +distclean:: clean + +prepare-includes: + -if [ ! -d include ]; then \ + cp -r ../linux/include include ; \ + ../v4l/scripts/headers_convert.pl `find include -type f` ; \ + fi diff --git a/v4l2-apps/lib/Makefile b/v4l2-apps/lib/Makefile index f123f3380..8443039b4 100644 --- a/v4l2-apps/lib/Makefile +++ b/v4l2-apps/lib/Makefile @@ -1,6 +1,6 @@ # Makefile for linuxtv.org v4l2-apps/lib -CPPFLAGS += -I../../linux/include -I.. +CPPFLAGS += -I../include -I.. includes = v4l2.h diff --git a/v4l2-apps/lib/frequencies.c b/v4l2-apps/lib/frequencies.c index 77a79e61c..64379d2b0 100644 --- a/v4l2-apps/lib/frequencies.c +++ b/v4l2-apps/lib/frequencies.c @@ -21,7 +21,7 @@ #include <stdlib.h> -#include "linux/videodev2.h" +#include <linux/videodev2.h> #include "v4l2.h" /* This source was originally written by Nathan Laredo <laredo@gnu.org>. diff --git a/v4l2-apps/lib/libv4l/ChangeLog b/v4l2-apps/lib/libv4l/ChangeLog index 286963543..eefe0028b 100644 --- a/v4l2-apps/lib/libv4l/ChangeLog +++ b/v4l2-apps/lib/libv4l/ChangeLog @@ -1,3 +1,61 @@ +libv4l-0.4.2 +------------ +* The bayer pixel order in gspca's sonixb driver was different from that in + the sn9c102 driver from the mainline kernel, a recent gspca patch fixes + this, adjust libv4l to match (and make it work properly with the sn9c102 + driver). + +libv4l-0.4.1 +------------ +* When the driver supports read() and we are not converting let the driver + handle read() instead of emulating it with mmap mode +* Fix errors and warnings when compiling with ICC (Gregor Jasny) +* Add support to libv4lconvert for rotating images 90 (for Pixart 7302 cams) + or 180 (Philips SPC200NC / Philips SPC300NC) degrees +* Add support for Pixart custom JPEG format +* Hide non public symbols (Gregor Jasny) +* Fix and enable x86_64 asm jpeg decompress helper functions (Gregor Jasny) + +libv4l-0.4.0 +------------ +* Be more relaxed in our checks for mixing read and mmap access, we were + being more strict in this then certain kernel drivers (bttv) making xawtv + unhappy +* With some drivers the buffers must be mapped before queuing, so when + converting map the (real) buffers before calling the qbuf ioctl +* Add support for conversion to RGB24 (before we only supported BGR24) based + on a patch by Jean-Francois Moine +* When the hardware supports a format natively prefer using the native + version over converting from another supported format +* Various Makefile and pkgconfig file improvements by Gregor Jasny (Debian) +* Drop the appl-patches dir, all application patches are now available and + tracked here: http://linuxtv.org/v4lwiki/index.php/Libv4l_Progress + + +libv4l-0.3.9 +------------ +* Not only see /dev/video* but also /dev/v4l/* as video devices + (only affects libv4l1 and the wrappers) patch from Brandon Philips +* Silence the creation of the .pc files in the Makefiles (Brandon Philips) +* Part of the copyright headers refered GPL instead of LGPL due to a copy + and paste error (Brandon Philips) + + +libv4l-0.3.8 +------------ +* work around wrong REQUEST_BUFFERS ioctl return code from certain drivers +* add pkg-config (.pc) files for easier detection if libv4l is available +* check capabilities for streaming, if the driver cannot do streaming don't + insert ourselves between the application and the driver +* intercept get capabilites and report read capability (which we always offer) +* query buffer: indicate the mapping state of our (fake) buffer in the flags + + +libv4l-0.3.7 +------------ +* Add spca505/6 and spca508 cam specific formats (YUYV per line variations) + + libv4l-0.3.6 ------------ * Add missing COPYING.LIB file diff --git a/v4l2-apps/lib/libv4l/Makefile b/v4l2-apps/lib/libv4l/Makefile index 0f4428eba..92928271a 100644 --- a/v4l2-apps/lib/libv4l/Makefile +++ b/v4l2-apps/lib/libv4l/Makefile @@ -1,10 +1,10 @@ LIB_RELEASE=0 -V4L2_LIB_VERSION=$(LIB_RELEASE).3.6 +V4L2_LIB_VERSION=$(LIB_RELEASE).4.2 all clean install: - $(MAKE) -C libv4lconvert $@ - $(MAKE) -C libv4l2 $@ - $(MAKE) -C libv4l1 $@ + $(MAKE) -C libv4lconvert V4L2_LIB_VERSION=$(V4L2_LIB_VERSION) $@ + $(MAKE) -C libv4l2 V4L2_LIB_VERSION=$(V4L2_LIB_VERSION) $@ + $(MAKE) -C libv4l1 V4L2_LIB_VERSION=$(V4L2_LIB_VERSION) $@ export: clean mkdir /tmp/libv4l-$(V4L2_LIB_VERSION) diff --git a/v4l2-apps/lib/libv4l/README b/v4l2-apps/lib/libv4l/README index b9b056f66..3a2059224 100644 --- a/v4l2-apps/lib/libv4l/README +++ b/v4l2-apps/lib/libv4l/README @@ -6,7 +6,7 @@ top of video4linux2 devices. The purpose of this (thin) layer is to make it easy for application writers to support a wide variety of devices without having to write seperate code for different devices in the same class. -All libv4l components are licensed under the GNU Library General Publishing +All libv4l components are licensed under the GNU Lesser General Public License version 2 or (at your option) any later version. libv4l consists of 3 different libraries: diff --git a/v4l2-apps/lib/libv4l/appl-patches/camorama-0.19-fixes.patch b/v4l2-apps/lib/libv4l/appl-patches/camorama-0.19-fixes.patch deleted file mode 100644 index 1e1333575..000000000 --- a/v4l2-apps/lib/libv4l/appl-patches/camorama-0.19-fixes.patch +++ /dev/null @@ -1,90 +0,0 @@ ---- camorama-0.19/src/callbacks.c 2007-09-16 15:36:55.000000000 +0200 -+++ camorama-0.19.new/src/callbacks.c 2008-06-29 22:22:44.000000000 +0200 -@@ -387,9 +387,6 @@ - } - } - -- cam->pixmap = gdk_pixmap_new (NULL, cam->x, cam->y, cam->desk_depth); -- gtk_widget_set_size_request (glade_xml_get_widget (cam->xml, "da"), -- cam->x, cam->y); - - /* - * if(cam->read == FALSE) { -@@ -441,6 +438,11 @@ - * * } - */ - get_win_info (cam); -+ -+ cam->pixmap = gdk_pixmap_new (NULL, cam->x, cam->y, cam->desk_depth); -+ gtk_widget_set_size_request (glade_xml_get_widget (cam->xml, "da"), -+ cam->x, cam->y); -+ - frame = 0; - gtk_window_resize (GTK_WINDOW - (glade_xml_get_widget (cam->xml, "main_window")), 320, -@@ -520,8 +522,14 @@ - gtk_widget_show (about); - } - -+void -+camorama_filter_color_filter(void* filter, guchar *image, int x, int y, int depth); -+ - static void - apply_filters(cam* cam) { -+ /* v4l has reverse rgb order from what camora expect so call the color -+ filter to fix things up before running the user selected filters */ -+ camorama_filter_color_filter(NULL, cam->pic_buf, cam->x, cam->y, cam->depth); - camorama_filter_chain_apply(cam->filter_chain, cam->pic_buf, cam->x, cam->y, cam->depth); - #warning "FIXME: enable the threshold channel filter" - // if((effect_mask & CAMORAMA_FILTER_THRESHOLD_CHANNEL) != 0) ---- camorama-0.19/src/filter.c 2007-09-16 14:48:50.000000000 +0200 -+++ camorama-0.19.new/src/filter.c 2008-06-29 22:11:42.000000000 +0200 -@@ -151,12 +151,12 @@ - static void - camorama_filter_color_init(CamoramaFilterColor* self) {} - --static void -+void - camorama_filter_color_filter(CamoramaFilterColor* filter, guchar *image, int x, int y, int depth) { - int i; - char tmp; - i = x * y; -- while (--i) { -+ while (i--) { - tmp = image[0]; - image[0] = image[2]; - image[2] = tmp; ---- camorama-0.19/src/main.c 2007-09-16 15:36:55.000000000 +0200 -+++ camorama-0.19.new/src/main.c 2008-06-29 22:20:04.000000000 +0200 -@@ -224,8 +224,7 @@ - - /* get picture attributes */ - get_pic_info (cam); --// set_pic_info(cam); -- /* set_pic_info(cam); */ -+ set_pic_info (cam); - cam->contrast = cam->vid_pic.contrast; - cam->brightness = cam->vid_pic.brightness; - cam->colour = cam->vid_pic.colour; ---- camorama-0.19/src/v4l.c 2007-09-16 14:48:05.000000000 +0200 -+++ camorama-0.19.new/src/v4l.c 2008-06-29 22:20:23.000000000 +0200 -@@ -158,8 +158,8 @@ - if(cam->debug) { - g_message("SET PIC"); - } -- //cam->vid_pic.palette = VIDEO_PALETTE_RGB24; -- //cam->vid_pic.depth = 24; -+ cam->vid_pic.palette = VIDEO_PALETTE_RGB24; -+ cam->vid_pic.depth = 24; - //cam->vid_pic.palette = VIDEO_PALETTE_YUV420P; - if(ioctl(cam->dev, VIDIOCSPICT, &cam->vid_pic) == -1) { - if(cam->debug) { -@@ -232,6 +232,8 @@ - exit(0); - } - -+ cam->x = cam->vid_win.width; -+ cam->y = cam->vid_win.height; - } - - void set_buffer(cam * cam) diff --git a/v4l2-apps/lib/libv4l/appl-patches/kdenetwork-4.0.85-kopete.patch b/v4l2-apps/lib/libv4l/appl-patches/kdenetwork-4.0.85-kopete.patch deleted file mode 100644 index b187f05e4..000000000 --- a/v4l2-apps/lib/libv4l/appl-patches/kdenetwork-4.0.85-kopete.patch +++ /dev/null @@ -1,12 +0,0 @@ -diff -up kdenetwork-4.0.85/kopete/libkopete/avdevice/videodevice.cpp~ kdenetwork-4.0.85/kopete/libkopete/avdevice/videodevice.cpp ---- kdenetwork-4.0.85/kopete/libkopete/avdevice/videodevice.cpp~ 2008-07-07 22:40:56.000000000 +0200 -+++ kdenetwork-4.0.85/kopete/libkopete/avdevice/videodevice.cpp 2008-07-07 22:40:56.000000000 +0200 -@@ -679,6 +679,8 @@ kDebug() << "VIDIOC_S_FMT worked (" << e - if (fmt.fmt.pix.sizeimage < min) - fmt.fmt.pix.sizeimage = min; - m_buffer_size=fmt.fmt.pix.sizeimage ; -+ currentwidth = fmt.fmt.pix.width; -+ currentheight = fmt.fmt.pix.height; - } - break; - #endif diff --git a/v4l2-apps/lib/libv4l/appl-patches/vlc-0.8.6-libv4l1.patch b/v4l2-apps/lib/libv4l/appl-patches/vlc-0.8.6-libv4l1.patch deleted file mode 100644 index 132549b55..000000000 --- a/v4l2-apps/lib/libv4l/appl-patches/vlc-0.8.6-libv4l1.patch +++ /dev/null @@ -1,319 +0,0 @@ -diff -up vlc-0.8.6f/modules/access/v4l/Makefile.am~ vlc-0.8.6f/modules/access/v4l/Makefile.am ---- vlc-0.8.6f/modules/access/v4l/Makefile.am~ 2008-06-29 17:14:11.000000000 +0200 -+++ vlc-0.8.6f/modules/access/v4l/Makefile.am 2008-06-29 17:16:39.000000000 +0200 -@@ -100,7 +100,7 @@ libv4l_plugin_la_CXXFLAGS = `$(VLC_CONFI - libv4l_plugin_la_OBJCFLAGS = `$(VLC_CONFIG) --objcflags plugin v4l` - libv4l_plugin_la_LDFLAGS = `$(VLC_CONFIG) --libs plugin v4l` \ - -rpath '$(libvlcdir)' -avoid-version -module -shrext $(LIBEXT) --libv4l_plugin_la_LIBADD = $(LTLIBVLC) -+libv4l_plugin_la_LIBADD = $(LTLIBVLC) -lv4l1 - - libv4l_a_SOURCES = $(SOURCES_v4l) - libv4l_builtin_la_SOURCES = $(SOURCES_v4l) -diff -up vlc-0.8.6f/modules/access/v4l/Makefile.in~ vlc-0.8.6f/modules/access/v4l/Makefile.in ---- vlc-0.8.6f/modules/access/v4l/Makefile.in~ 2008-06-29 17:16:22.000000000 +0200 -+++ vlc-0.8.6f/modules/access/v4l/Makefile.in 2008-06-29 17:16:42.000000000 +0200 -@@ -390,7 +390,7 @@ libv4l_plugin_la_OBJCFLAGS = `$(VLC_CONF - libv4l_plugin_la_LDFLAGS = `$(VLC_CONFIG) --libs plugin v4l` \ - -rpath '$(libvlcdir)' -avoid-version -module -shrext $(LIBEXT) - --libv4l_plugin_la_LIBADD = $(LTLIBVLC) -+libv4l_plugin_la_LIBADD = $(LTLIBVLC) -lv4l1 - libv4l_a_SOURCES = $(SOURCES_v4l) - libv4l_builtin_la_SOURCES = $(SOURCES_v4l) - libv4l_a_CFLAGS = `$(VLC_CONFIG) --cflags builtin pic v4l` -diff -up vlc-0.8.6f/modules/access/v4l/v4l.c~ vlc-0.8.6f/modules/access/v4l/v4l.c ---- vlc-0.8.6f/modules/access/v4l/v4l.c~ 2008-06-29 17:13:30.000000000 +0200 -+++ vlc-0.8.6f/modules/access/v4l/v4l.c 2008-06-29 17:13:30.000000000 +0200 -@@ -64,6 +64,9 @@ - - #include <sys/soundcard.h> - -+#include <libv4l1.h> -+ -+ - /***************************************************************************** - * Module descriptior - *****************************************************************************/ -@@ -546,23 +549,23 @@ static void Close( vlc_object_t *p_this - if( p_sys->psz_device ) free( p_sys->psz_device ); - if( p_sys->psz_vdev ) free( p_sys->psz_vdev ); - if( p_sys->psz_adev ) free( p_sys->psz_adev ); -- if( p_sys->fd_video >= 0 ) close( p_sys->fd_video ); -+ if( p_sys->fd_video >= 0 ) v4l1_close( p_sys->fd_video ); - if( p_sys->fd_audio >= 0 ) close( p_sys->fd_audio ); - if( p_sys->p_block_audio ) block_Release( p_sys->p_block_audio ); - - if( p_sys->b_mjpeg ) - { - int i_noframe = -1; -- ioctl( p_sys->fd_video, MJPIOC_QBUF_CAPT, &i_noframe ); -+ v4l1_ioctl( p_sys->fd_video, MJPIOC_QBUF_CAPT, &i_noframe ); - } - - if( p_sys->p_video_mmap && p_sys->p_video_mmap != MAP_FAILED ) - { - if( p_sys->b_mjpeg ) -- munmap( p_sys->p_video_mmap, p_sys->mjpeg_buffers.size * -+ v4l1_munmap( p_sys->p_video_mmap, p_sys->mjpeg_buffers.size * - p_sys->mjpeg_buffers.count ); - else -- munmap( p_sys->p_video_mmap, p_sys->vid_mbuf.size ); -+ v4l1_munmap( p_sys->p_video_mmap, p_sys->vid_mbuf.size ); - } - - free( p_sys ); -@@ -875,13 +878,13 @@ static int OpenVideoDev( demux_t *p_demu - struct mjpeg_params mjpeg; - int i; - -- if( ( i_fd = open( psz_device, O_RDWR ) ) < 0 ) -+ if( ( i_fd = v4l1_open( psz_device, O_RDWR ) ) < 0 ) - { - msg_Err( p_demux, "cannot open device (%s)", strerror( errno ) ); - goto vdev_failed; - } - -- if( ioctl( i_fd, VIDIOCGCAP, &p_sys->vid_cap ) < 0 ) -+ if( v4l1_ioctl( i_fd, VIDIOCGCAP, &p_sys->vid_cap ) < 0 ) - { - msg_Err( p_demux, "cannot get capabilities (%s)", strerror( errno ) ); - goto vdev_failed; -@@ -926,7 +929,7 @@ static int OpenVideoDev( demux_t *p_demu - } - - vid_channel.channel = p_sys->i_channel; -- if( ioctl( i_fd, VIDIOCGCHAN, &vid_channel ) < 0 ) -+ if( v4l1_ioctl( i_fd, VIDIOCGCHAN, &vid_channel ) < 0 ) - { - msg_Err( p_demux, "cannot get channel infos (%s)", - strerror( errno ) ); -@@ -944,7 +947,7 @@ static int OpenVideoDev( demux_t *p_demu - } - - vid_channel.norm = p_sys->i_norm; -- if( ioctl( i_fd, VIDIOCSCHAN, &vid_channel ) < 0 ) -+ if( v4l1_ioctl( i_fd, VIDIOCSCHAN, &vid_channel ) < 0 ) - { - msg_Err( p_demux, "cannot set channel (%s)", strerror( errno ) ); - goto vdev_failed; -@@ -959,7 +962,7 @@ static int OpenVideoDev( demux_t *p_demu - if( p_sys->i_tuner >= 0 ) - { - vid_tuner.tuner = p_sys->i_tuner; -- if( ioctl( i_fd, VIDIOCGTUNER, &vid_tuner ) < 0 ) -+ if( v4l1_ioctl( i_fd, VIDIOCGTUNER, &vid_tuner ) < 0 ) - { - msg_Err( p_demux, "cannot get tuner (%s)", strerror( errno ) ); - goto vdev_failed; -@@ -974,7 +977,7 @@ static int OpenVideoDev( demux_t *p_demu - - /* FIXME FIXME to be checked FIXME FIXME */ - //vid_tuner.mode = p_sys->i_norm; -- if( ioctl( i_fd, VIDIOCSTUNER, &vid_tuner ) < 0 ) -+ if( v4l1_ioctl( i_fd, VIDIOCSTUNER, &vid_tuner ) < 0 ) - { - msg_Err( p_demux, "cannot set tuner (%s)", strerror( errno ) ); - goto vdev_failed; -@@ -990,7 +993,7 @@ static int OpenVideoDev( demux_t *p_demu - if( p_sys->i_frequency >= 0 ) - { - int driver_frequency = p_sys->i_frequency * 16 /1000; -- if( ioctl( i_fd, VIDIOCSFREQ, &driver_frequency ) < 0 ) -+ if( v4l1_ioctl( i_fd, VIDIOCSFREQ, &driver_frequency ) < 0 ) - { - msg_Err( p_demux, "cannot set frequency (%s)", - strerror( errno ) ); -@@ -1010,7 +1013,7 @@ static int OpenVideoDev( demux_t *p_demu - if( p_sys->i_audio >= 0 ) - { - vid_audio.audio = p_sys->i_audio; -- if( ioctl( i_fd, VIDIOCGAUDIO, &vid_audio ) < 0 ) -+ if( v4l1_ioctl( i_fd, VIDIOCGAUDIO, &vid_audio ) < 0 ) - { - msg_Err( p_demux, "cannot get audio (%s)", strerror( errno ) ); - goto vdev_failed; -@@ -1019,7 +1022,7 @@ static int OpenVideoDev( demux_t *p_demu - /* unmute audio */ - vid_audio.flags &= ~VIDEO_AUDIO_MUTE; - -- if( ioctl( i_fd, VIDIOCSAUDIO, &vid_audio ) < 0 ) -+ if( v4l1_ioctl( i_fd, VIDIOCSAUDIO, &vid_audio ) < 0 ) - { - msg_Err( p_demux, "cannot set audio (%s)", strerror( errno ) ); - goto vdev_failed; -@@ -1035,7 +1038,7 @@ static int OpenVideoDev( demux_t *p_demu - struct quicktime_mjpeg_app1 *p_app1; - int32_t i_offset; - -- if( ioctl( i_fd, MJPIOC_G_PARAMS, &mjpeg ) < 0 ) -+ if( v4l1_ioctl( i_fd, MJPIOC_G_PARAMS, &mjpeg ) < 0 ) - { - msg_Err( p_demux, "cannot get mjpeg params (%s)", - strerror( errno ) ); -@@ -1086,7 +1089,7 @@ static int OpenVideoDev( demux_t *p_demu - * optional. They will be present in the output. */ - mjpeg.jpeg_markers = JPEG_MARKER_DHT | JPEG_MARKER_DQT; - -- if( ioctl( i_fd, MJPIOC_S_PARAMS, &mjpeg ) < 0 ) -+ if( v4l1_ioctl( i_fd, MJPIOC_S_PARAMS, &mjpeg ) < 0 ) - { - msg_Err( p_demux, "cannot set mjpeg params (%s)", - strerror( errno ) ); -@@ -1103,7 +1106,7 @@ static int OpenVideoDev( demux_t *p_demu - { - struct video_window vid_win; - -- if( ioctl( i_fd, VIDIOCGWIN, &vid_win ) < 0 ) -+ if( v4l1_ioctl( i_fd, VIDIOCGWIN, &vid_win ) < 0 ) - { - msg_Err( p_demux, "cannot get win (%s)", strerror( errno ) ); - goto vdev_failed; -@@ -1130,7 +1133,7 @@ static int OpenVideoDev( demux_t *p_demu - if( !p_sys->b_mjpeg ) - { - /* set hue/color/.. */ -- if( ioctl( i_fd, VIDIOCGPICT, &p_sys->vid_picture ) == 0 ) -+ if( v4l1_ioctl( i_fd, VIDIOCGPICT, &p_sys->vid_picture ) == 0 ) - { - struct video_picture vid_picture = p_sys->vid_picture; - -@@ -1150,7 +1153,7 @@ static int OpenVideoDev( demux_t *p_demu - { - vid_picture.contrast = p_sys->i_contrast; - } -- if( ioctl( i_fd, VIDIOCSPICT, &vid_picture ) == 0 ) -+ if( v4l1_ioctl( i_fd, VIDIOCSPICT, &vid_picture ) == 0 ) - { - msg_Dbg( p_demux, "v4l device uses brightness: %d", - vid_picture.brightness ); -@@ -1164,7 +1167,7 @@ static int OpenVideoDev( demux_t *p_demu - } - - /* Find out video format used by device */ -- if( ioctl( i_fd, VIDIOCGPICT, &p_sys->vid_picture ) == 0 ) -+ if( v4l1_ioctl( i_fd, VIDIOCGPICT, &p_sys->vid_picture ) == 0 ) - { - struct video_picture vid_picture = p_sys->vid_picture; - char *psz; -@@ -1191,7 +1194,7 @@ static int OpenVideoDev( demux_t *p_demu - free( psz ); - - if( vid_picture.palette && -- !ioctl( i_fd, VIDIOCSPICT, &vid_picture ) ) -+ !v4l1_ioctl( i_fd, VIDIOCSPICT, &vid_picture ) ) - { - p_sys->vid_picture = vid_picture; - } -@@ -1199,14 +1202,14 @@ static int OpenVideoDev( demux_t *p_demu - { - /* Try to set the format to something easy to encode */ - vid_picture.palette = VIDEO_PALETTE_YUV420P; -- if( ioctl( i_fd, VIDIOCSPICT, &vid_picture ) == 0 ) -+ if( v4l1_ioctl( i_fd, VIDIOCSPICT, &vid_picture ) == 0 ) - { - p_sys->vid_picture = vid_picture; - } - else - { - vid_picture.palette = VIDEO_PALETTE_YUV422P; -- if( ioctl( i_fd, VIDIOCSPICT, &vid_picture ) == 0 ) -+ if( v4l1_ioctl( i_fd, VIDIOCSPICT, &vid_picture ) == 0 ) - { - p_sys->vid_picture = vid_picture; - } -@@ -1237,13 +1240,13 @@ static int OpenVideoDev( demux_t *p_demu - p_sys->mjpeg_buffers.count = 8; - p_sys->mjpeg_buffers.size = MJPEG_BUFFER_SIZE; - -- if( ioctl( i_fd, MJPIOC_REQBUFS, &p_sys->mjpeg_buffers ) < 0 ) -+ if( v4l1_ioctl( i_fd, MJPIOC_REQBUFS, &p_sys->mjpeg_buffers ) < 0 ) - { - msg_Err( p_demux, "mmap unsupported" ); - goto vdev_failed; - } - -- p_sys->p_video_mmap = mmap( 0, -+ p_sys->p_video_mmap = v4l1_mmap( 0, - p_sys->mjpeg_buffers.size * p_sys->mjpeg_buffers.count, - PROT_READ | PROT_WRITE, MAP_SHARED, i_fd, 0 ); - if( p_sys->p_video_mmap == MAP_FAILED ) -@@ -1258,7 +1261,7 @@ static int OpenVideoDev( demux_t *p_demu - /* queue up all the frames */ - for( i = 0; i < (int)p_sys->mjpeg_buffers.count; i++ ) - { -- if( ioctl( i_fd, MJPIOC_QBUF_CAPT, &i ) < 0 ) -+ if( v4l1_ioctl( i_fd, MJPIOC_QBUF_CAPT, &i ) < 0 ) - { - msg_Err( p_demux, "unable to queue frame" ); - goto vdev_failed; -@@ -1289,13 +1292,13 @@ static int OpenVideoDev( demux_t *p_demu - (char*)&p_sys->i_fourcc ); - - /* Allocate mmap buffer */ -- if( ioctl( i_fd, VIDIOCGMBUF, &p_sys->vid_mbuf ) < 0 ) -+ if( v4l1_ioctl( i_fd, VIDIOCGMBUF, &p_sys->vid_mbuf ) < 0 ) - { - msg_Err( p_demux, "mmap unsupported" ); - goto vdev_failed; - } - -- p_sys->p_video_mmap = mmap( 0, p_sys->vid_mbuf.size, -+ p_sys->p_video_mmap = v4l1_mmap( 0, p_sys->vid_mbuf.size, - PROT_READ|PROT_WRITE, MAP_SHARED, - i_fd, 0 ); - if( p_sys->p_video_mmap == MAP_FAILED ) -@@ -1310,7 +1313,7 @@ static int OpenVideoDev( demux_t *p_demu - p_sys->vid_mmap.width = p_sys->i_width; - p_sys->vid_mmap.height = p_sys->i_height; - p_sys->vid_mmap.format = p_sys->vid_picture.palette; -- if( ioctl( i_fd, VIDIOCMCAPTURE, &p_sys->vid_mmap ) < 0 ) -+ if( v4l1_ioctl( i_fd, VIDIOCMCAPTURE, &p_sys->vid_mmap ) < 0 ) - { - msg_Warn( p_demux, "%4.4s refused", (char*)&p_sys->i_fourcc ); - msg_Err( p_demux, "chroma selection failed" ); -@@ -1321,7 +1324,7 @@ static int OpenVideoDev( demux_t *p_demu - - vdev_failed: - -- if( i_fd >= 0 ) close( i_fd ); -+ if( i_fd >= 0 ) v4l1_close( i_fd ); - return -1; - } - -@@ -1431,7 +1434,7 @@ static uint8_t *GrabCapture( demux_t *p_ - - p_sys->vid_mmap.frame = (p_sys->i_frame_pos + 1) % p_sys->vid_mbuf.frames; - -- while( ioctl( p_sys->fd_video, VIDIOCMCAPTURE, &p_sys->vid_mmap ) < 0 ) -+ while( v4l1_ioctl( p_sys->fd_video, VIDIOCMCAPTURE, &p_sys->vid_mmap ) < 0 ) - { - if( errno != EAGAIN ) - { -@@ -1447,7 +1450,7 @@ static uint8_t *GrabCapture( demux_t *p_ - msg_Dbg( p_demux, "grab failed, trying again" ); - } - -- while( ioctl(p_sys->fd_video, VIDIOCSYNC, &p_sys->i_frame_pos) < 0 ) -+ while( v4l1_ioctl(p_sys->fd_video, VIDIOCSYNC, &p_sys->i_frame_pos) < 0 ) - { - if( errno != EAGAIN && errno != EINTR ) - { -@@ -1473,7 +1476,7 @@ static uint8_t *GrabMJPEG( demux_t *p_de - /* re-queue the last frame we sync'd */ - if( p_sys->i_frame_pos != -1 ) - { -- while( ioctl( p_sys->fd_video, MJPIOC_QBUF_CAPT, -+ while( v4l1_ioctl( p_sys->fd_video, MJPIOC_QBUF_CAPT, - &p_sys->i_frame_pos ) < 0 ) - { - if( errno != EAGAIN && errno != EINTR ) -@@ -1485,7 +1488,7 @@ static uint8_t *GrabMJPEG( demux_t *p_de - } - - /* sync on the next frame */ -- while( ioctl( p_sys->fd_video, MJPIOC_SYNC, &sync ) < 0 ) -+ while( v4l1_ioctl( p_sys->fd_video, MJPIOC_SYNC, &sync ) < 0 ) - { - if( errno != EAGAIN && errno != EINTR ) - { diff --git a/v4l2-apps/lib/libv4l/appl-patches/xawtv-3.95-fixes.patch b/v4l2-apps/lib/libv4l/appl-patches/xawtv-3.95-fixes.patch deleted file mode 100644 index ccb077be0..000000000 --- a/v4l2-apps/lib/libv4l/appl-patches/xawtv-3.95-fixes.patch +++ /dev/null @@ -1,29 +0,0 @@ ---- xawtv-3.95/libng/plugins/drv0-v4l2.c 2005-02-11 18:56:24.000000000 +0100 -+++ xawtv-3.95.new/libng/plugins/drv0-v4l2.c 2008-07-05 21:12:37.000000000 +0200 -@@ -161,7 +161,7 @@ - #define PREFIX "ioctl: " - - static int --xioctl(int fd, int cmd, void *arg, int mayfail) -+xioctl(int fd, unsigned long int cmd, void *arg, int mayfail) - { - int rc; - -@@ -768,6 +768,7 @@ - /* get it */ - memset(&buf,0,sizeof(buf)); - buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; -+ buf.memory = V4L2_MEMORY_MMAP; - if (-1 == xioctl(h->fd,VIDIOC_DQBUF,&buf, 0)) - return -1; - h->waiton++; -@@ -813,8 +814,7 @@ - if (-1 == xioctl(h->fd, VIDIOC_QUERYBUF, &h->buf_v4l2[i], 0)) - return -1; - h->buf_me[i].fmt = h->fmt_me; -- h->buf_me[i].size = h->buf_me[i].fmt.bytesperline * -- h->buf_me[i].fmt.height; -+ h->buf_me[i].size = h->buf_v4l2[i].length; - h->buf_me[i].data = mmap(NULL, h->buf_v4l2[i].length, - PROT_READ | PROT_WRITE, MAP_SHARED, - h->fd, h->buf_v4l2[i].m.offset); diff --git a/v4l2-apps/lib/libv4l/include/libv4l1.h b/v4l2-apps/lib/libv4l/include/libv4l1.h index 9036ae869..c878cc198 100644 --- a/v4l2-apps/lib/libv4l/include/libv4l1.h +++ b/v4l2-apps/lib/libv4l/include/libv4l1.h @@ -26,9 +26,15 @@ extern "C" { #include <stdio.h> #include <unistd.h> +#if __GNUC__ >= 4 +#define LIBV4L_PUBLIC __attribute__ ((visibility("default"))) +#else +#define LIBV4L_PUBLIC +#endif + /* Point this to a FILE opened for writing when you want to log error and status messages to a file, when NULL errors will get send to stderr */ -extern FILE *v4l1_log_file; +LIBV4L_PUBLIC extern FILE *v4l1_log_file; /* Just like your regular open/close/etc, except that when opening a v4l2 capture only device, full v4l1 emulation is done including emulating the @@ -51,14 +57,14 @@ extern FILE *v4l1_log_file; capture formats, like hw specific bayer compression methods). */ -int v4l1_open (const char *file, int oflag, ...); -int v4l1_close(int fd); -int v4l1_dup(int fd); -int v4l1_ioctl (int fd, unsigned long int request, ...); -ssize_t v4l1_read (int fd, void* buffer, size_t n); -void *v4l1_mmap(void *start, size_t length, int prot, int flags, int fd, +LIBV4L_PUBLIC int v4l1_open (const char *file, int oflag, ...); +LIBV4L_PUBLIC int v4l1_close(int fd); +LIBV4L_PUBLIC int v4l1_dup(int fd); +LIBV4L_PUBLIC int v4l1_ioctl (int fd, unsigned long int request, ...); +LIBV4L_PUBLIC ssize_t v4l1_read (int fd, void* buffer, size_t n); +LIBV4L_PUBLIC void *v4l1_mmap(void *start, size_t length, int prot, int flags, int fd, __off64_t offset); -int v4l1_munmap(void *_start, size_t length); +LIBV4L_PUBLIC int v4l1_munmap(void *_start, size_t length); #ifdef __cplusplus } diff --git a/v4l2-apps/lib/libv4l/include/libv4l2.h b/v4l2-apps/lib/libv4l/include/libv4l2.h index 63529cf4b..b05b57cb6 100644 --- a/v4l2-apps/lib/libv4l/include/libv4l2.h +++ b/v4l2-apps/lib/libv4l/include/libv4l2.h @@ -26,9 +26,15 @@ extern "C" { #endif /* __cplusplus */ +#if __GNUC__ >= 4 +#define LIBV4L_PUBLIC __attribute__ ((visibility("default"))) +#else +#define LIBV4L_PUBLIC +#endif + /* Point this to a FILE opened for writing when you want to log error and status messages to a file, when NULL errors will get send to stderr */ -extern FILE *v4l2_log_file; +LIBV4L_PUBLIC extern FILE *v4l2_log_file; /* Just like your regular open/close/etc, except that format conversion is done if necessary when capturing. That is if you (try to) set a capture @@ -52,14 +58,14 @@ extern FILE *v4l2_log_file; fail. */ -int v4l2_open (const char *file, int oflag, ...); -int v4l2_close(int fd); -int v4l2_dup(int fd); -int v4l2_ioctl (int fd, unsigned long int request, ...); -ssize_t v4l2_read (int fd, void* buffer, size_t n); -void *v4l2_mmap(void *start, size_t length, int prot, int flags, int fd, +LIBV4L_PUBLIC int v4l2_open (const char *file, int oflag, ...); +LIBV4L_PUBLIC int v4l2_close(int fd); +LIBV4L_PUBLIC int v4l2_dup(int fd); +LIBV4L_PUBLIC int v4l2_ioctl (int fd, unsigned long int request, ...); +LIBV4L_PUBLIC ssize_t v4l2_read (int fd, void* buffer, size_t n); +LIBV4L_PUBLIC void *v4l2_mmap(void *start, size_t length, int prot, int flags, int fd, __off64_t offset); -int v4l2_munmap(void *_start, size_t length); +LIBV4L_PUBLIC int v4l2_munmap(void *_start, size_t length); /* Misc utility functions */ @@ -70,12 +76,12 @@ int v4l2_munmap(void *_start, size_t length); Normally returns 0, even if the cid did not exist or was locked, returns non 0 when an other error occured. */ -int v4l2_set_control(int fd, int cid, int value); +LIBV4L_PUBLIC int v4l2_set_control(int fd, int cid, int value); /* This function returns a value of 0 - 65535, scaled to from the actual range of the given v4l control id. when the cid does not exist, could not be accessed for some reason, or some error occured 0 is returned. */ -int v4l2_get_control(int fd, int cid); +LIBV4L_PUBLIC int v4l2_get_control(int fd, int cid); /* "low level" access functions, these functions allow somewhat lower level @@ -95,7 +101,7 @@ int v4l2_get_control(int fd, int cid); Returns fd on success, -1 if the fd is not suitable for use through libv4l2 (note the fd is left open in this case). */ -int v4l2_fd_open(int fd, int v4l2_flags); +LIBV4L_PUBLIC int v4l2_fd_open(int fd, int v4l2_flags); #ifdef __cplusplus } diff --git a/v4l2-apps/lib/libv4l/include/libv4lconvert.h b/v4l2-apps/lib/libv4l/include/libv4lconvert.h index d8446dac8..87e41ceaa 100644 --- a/v4l2-apps/lib/libv4l/include/libv4lconvert.h +++ b/v4l2-apps/lib/libv4l/include/libv4lconvert.h @@ -31,34 +31,45 @@ extern "C" { #endif /* __cplusplus */ +#if __GNUC__ >= 4 +#define LIBV4L_PUBLIC __attribute__ ((visibility("default"))) +#else +#define LIBV4L_PUBLIC +#endif + struct v4lconvert_data; -struct v4lconvert_data *v4lconvert_create(int fd); -void v4lconvert_destroy(struct v4lconvert_data *data); +LIBV4L_PUBLIC struct v4lconvert_data *v4lconvert_create(int fd); +LIBV4L_PUBLIC void v4lconvert_destroy(struct v4lconvert_data *data); /* With regards to dest_fmt just like VIDIOC_TRY_FMT, except that the try format will succeed and return the requested V4L2_PIX_FMT_foo in dest_fmt if the cam has a format from which v4lconvert can convert to dest_fmt. The real format to which the cam should be set is returned through src_fmt when not NULL. */ -int v4lconvert_try_format(struct v4lconvert_data *data, +LIBV4L_PUBLIC int v4lconvert_try_format(struct v4lconvert_data *data, struct v4l2_format *dest_fmt, /* in / out */ struct v4l2_format *src_fmt /* out */ ); /* Just like VIDIOC_ENUM_FMT, except that the emulated formats are added at the end of the list */ -int v4lconvert_enum_fmt(struct v4lconvert_data *data, struct v4l2_fmtdesc *fmt); +LIBV4L_PUBLIC int v4lconvert_enum_fmt(struct v4lconvert_data *data, struct v4l2_fmtdesc *fmt); + +/* Is conversion necessary or can the app use the data directly? */ +LIBV4L_PUBLIC int v4lconvert_needs_conversion(struct v4lconvert_data *data, + const struct v4l2_format *src_fmt, /* in */ + const struct v4l2_format *dest_fmt); /* in */ /* return value of -1 on error, otherwise the amount of bytes written to dest */ -int v4lconvert_convert(struct v4lconvert_data *data, +LIBV4L_PUBLIC int v4lconvert_convert(struct v4lconvert_data *data, const struct v4l2_format *src_fmt, /* in */ const struct v4l2_format *dest_fmt, /* in */ unsigned char *src, int src_size, unsigned char *dest, int dest_size); /* get a string describing the last error*/ -const char *v4lconvert_get_error_message(struct v4lconvert_data *data); +LIBV4L_PUBLIC const char *v4lconvert_get_error_message(struct v4lconvert_data *data); #ifdef __cplusplus } diff --git a/v4l2-apps/lib/libv4l/libv4l1/Makefile b/v4l2-apps/lib/libv4l/libv4l1/Makefile index 8cb064cd4..27848477e 100644 --- a/v4l2-apps/lib/libv4l/libv4l1/Makefile +++ b/v4l2-apps/lib/libv4l/libv4l1/Makefile @@ -1,20 +1,25 @@ -CC = gcc -LD = gcc - -CPPFLAGS = -fPIC -I../include -I../../../../linux/include +override CPPFLAGS += -I../include -I../../../include -fvisibility=hidden CFLAGS := -g -O1 -CFLAGS += -Wall -W -Wno-unused -Wpointer-arith -Wstrict-prototypes +CFLAGS += -Wall -Wno-unused -Wpointer-arith -Wstrict-prototypes -Wmissing-prototypes -LDFLAGS = -shared +LIBS = -lpthread -V4L1_OBJS = libv4l1.o log.o ../libv4l2/libv4l2.so -V4L1_LIB = libv4l1.so +V4L1_OBJS = libv4l1.o log.o V4L1COMPAT = v4l1compat.so V4L1COMPAT_O = v4l1compat.o libv4l1.so -TARGETS = $(V4L1_LIB) $(V4L1COMPAT) +TARGETS = $(V4L1_LIB) libv4l1.pc INCLUDES = ../include/libv4l1.h +ifeq ($(LINKTYPE),static) +V4L1_LIB = libv4l1.a +else +V4L1_LIB = libv4l1.so +V4L1_OBJS += ../libv4l2/libv4l2.so +TARGETS += $(V4L1COMPAT) +override CPPFLAGS += -fPIC +endif + ifeq ($(LIB_RELEASE),) LIB_RELEASE = 0 endif @@ -34,23 +39,45 @@ $(V4L1_LIB): $(V4L1_OBJS) $(V4L1COMPAT): $(V4L1COMPAT_O) $(V4L1_LIB) +libv4l1.pc: + @echo prefix=$(PREFIX) > libv4l1.pc + @echo libdir=$(LIBDIR) >> libv4l1.pc + @echo >> libv4l1.pc + @echo 'Name: libv4l1' >> libv4l1.pc + @echo 'Description: v4l1 compatibility library' >> libv4l1.pc + @echo 'Version: '$(V4L2_LIB_VERSION) >> libv4l1.pc + @echo 'Requires: libv4l2' >> libv4l1.pc + @echo 'Libs: -L$${libdir} -lv4l1' >> libv4l1.pc + @echo 'Libs.private: -lpthread' >> libv4l1.pc + @echo 'Cflags: -I$${prefix}/include' >> libv4l1.pc + install: all mkdir -p $(DESTDIR)$(PREFIX)/include install -p -m 644 $(INCLUDES) $(DESTDIR)$(PREFIX)/include +ifeq ($(LINKTYPE),static) + mkdir -p $(DESTDIR)$(LIBDIR) + install -m 644 $(V4L1_LIB) $(DESTDIR)$(LIBDIR) +else mkdir -p $(DESTDIR)$(LIBDIR)/libv4l 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) +endif + mkdir -p $(DESTDIR)$(LIBDIR)/pkgconfig + install -m 644 libv4l1.pc $(DESTDIR)$(LIBDIR)/pkgconfig clean:: - rm -f *.so* *.o log *~ - rm -f *.d + rm -f *.a *.so* *.o *.d libv4l1.pc log *~ %.o: %.c $(CC) -c -MMD $(CPPFLAGS) $(CFLAGS) -o $@ $< %.so: - $(CC) $(LDFLAGS) -Wl,-soname,$@.$(LIB_RELEASE) -o $@.$(LIB_RELEASE) $^ + $(CC) -shared $(LDFLAGS) -Wl,-soname,$@.$(LIB_RELEASE) -o $@.$(LIB_RELEASE) $^ $(LIBS) ln -f -s $@.$(LIB_RELEASE) $@ + +%.a: + $(AR) cqs $@ $^ + diff --git a/v4l2-apps/lib/libv4l/libv4l1/libv4l1-priv.h b/v4l2-apps/lib/libv4l/libv4l1/libv4l1-priv.h index e09041256..651599255 100644 --- a/v4l2-apps/lib/libv4l/libv4l1/libv4l1-priv.h +++ b/v4l2-apps/lib/libv4l/libv4l1/libv4l1-priv.h @@ -9,7 +9,7 @@ # 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. +# 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 diff --git a/v4l2-apps/lib/libv4l/libv4l1/libv4l1.c b/v4l2-apps/lib/libv4l/libv4l1/libv4l1.c index b80fcb56d..39f2d29e0 100644 --- a/v4l2-apps/lib/libv4l/libv4l1/libv4l1.c +++ b/v4l2-apps/lib/libv4l/libv4l1/libv4l1.c @@ -28,12 +28,12 @@ capture only devices, or non v4l2 devices. 2) libv4l1 is the base of the v4l1compat.so wrapper lib, which is a .so which can be LD_PRELOAD-ed and the overrules the libc's open/close/etc, - and when opening /dev/videoX calls v4l1_open. Because we behave as the - regular counterpart when the fd is not known (instead of say throwing - an error), v4l1compat.so can simply call the v4l1_ prefixed function - for all wrapped functions. This way the wrapper does not have to keep - track of which fd's are being handled by libv4l1, as libv4l1 already - keeps track of this itself. + and when opening /dev/videoX or /dev/v4l/ calls v4l1_open. Because we + behave as the regular counterpart when the fd is not known (instead of + say throwing an error), v4l1compat.so can simply call the v4l1_ prefixed + function for all wrapped functions. This way the wrapper does not have + to keep track of which fd's are being handled by libv4l1, as libv4l1 + already keeps track of this itself. This also means that libv4l1 may not use any of the regular functions it mimics, as for example open could be a symbol in v4l1compat.so, which @@ -59,6 +59,7 @@ #include <linux/videodev.h> #include <linux/videodev2.h> #include <libv4l2.h> +#include "libv4l1.h" #include "libv4l1-priv.h" #define V4L1_SUPPORTS_ENUMINPUT 0x01 @@ -279,7 +280,7 @@ int v4l1_open (const char *file, int oflag, ...) return fd; /* check if we're opening a video4linux2 device */ - if (strncmp(file, "/dev/video", 10)) + if (strncmp(file, "/dev/video", 10) && strncmp(file, "/dev/v4l/", 9)) return fd; /* check that this is an v4l2 device, no need to emulate v4l1 on @@ -582,7 +583,7 @@ int v4l1_ioctl (int fd, unsigned long int request, ...) input2.index = chan->channel; result = v4l2_ioctl(fd, VIDIOC_ENUMINPUT, &input2); if (result == 0) { - snprintf(chan->name, sizeof(chan->name), "%s", input2.name); + snprintf(chan->name, sizeof(chan->name), "%s", (char*)input2.name); if (input2.type == V4L2_INPUT_TYPE_TUNER) { chan->tuners = 1; chan->type = VIDEO_TYPE_TV; diff --git a/v4l2-apps/lib/libv4l/libv4l1/log.c b/v4l2-apps/lib/libv4l/libv4l1/log.c index 178c6d23c..74ce0f20b 100644 --- a/v4l2-apps/lib/libv4l/libv4l1/log.c +++ b/v4l2-apps/lib/libv4l/libv4l1/log.c @@ -9,7 +9,7 @@ # 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. +# 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 @@ -25,6 +25,7 @@ #include <asm/types.h> /* end broken header workaround includes */ #include <linux/videodev.h> +#include "libv4l1-priv.h" #define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0])) @@ -92,9 +93,9 @@ void v4l1_log_ioctl(unsigned long int request, void *arg, int result) break; case VIDIOCGWIN: case VIDIOCSWIN: - fprintf(v4l1_log_file,"width\t%d\n", + fprintf(v4l1_log_file,"width\t%u\n", ((struct video_window *)arg)->width); - fprintf(v4l1_log_file,"height\t%d\n", + fprintf(v4l1_log_file,"height\t%u\n", ((struct video_window *)arg)->height); break; @@ -115,7 +116,7 @@ void v4l1_log_ioctl(unsigned long int request, void *arg, int result) fprintf(v4l1_log_file,"palette %d\n",( (int)((struct video_picture*)arg)->palette) ); break; - case VIDIOCCAPTURE: fprintf(v4l1_log_file,"on/of? %d\n", *((int *)arg) ); + case VIDIOCCAPTURE: fprintf(v4l1_log_file,"on/off? %d\n", *((int *)arg) ); break; case VIDIOCSYNC: fprintf(v4l1_log_file,"sync %d\n", *((int *)arg) ); diff --git a/v4l2-apps/lib/libv4l/libv4l1/v4l1compat.c b/v4l2-apps/lib/libv4l/libv4l1/v4l1compat.c index db3a0027b..e4293d2f9 100644 --- a/v4l2-apps/lib/libv4l/libv4l1/v4l1compat.c +++ b/v4l2-apps/lib/libv4l/libv4l1/v4l1compat.c @@ -12,7 +12,7 @@ # 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. +# 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 @@ -26,12 +26,21 @@ #include <fcntl.h> #include <libv4l1.h> +#include <sys/ioctl.h> +#include <sys/mman.h> + /* Check that open/read/mmap is not a define */ #if defined open || defined read || defined mmap #error open/read/mmap is a prepocessor macro !! #endif -int open (const char *file, int oflag, ...) +#if __GNUC__ >= 4 +#define LIBV4L_PUBLIC __attribute__ ((visibility("default"))) +#else +#define LIBV4L_PUBLIC +#endif + +LIBV4L_PUBLIC int open (const char *file, int oflag, ...) { int fd; @@ -52,7 +61,7 @@ int open (const char *file, int oflag, ...) return fd; } -int open64 (const char *file, int oflag, ...) +LIBV4L_PUBLIC int open64 (const char *file, int oflag, ...) { int fd; @@ -73,16 +82,16 @@ int open64 (const char *file, int oflag, ...) return fd; } -int close(int fd) { +LIBV4L_PUBLIC int close(int fd) { return v4l1_close(fd); } -int dup(int fd) +LIBV4L_PUBLIC int dup(int fd) { return v4l1_dup(fd); } -int ioctl (int fd, unsigned long int request, ...) +LIBV4L_PUBLIC int ioctl (int fd, unsigned long int request, ...) { void *arg; va_list ap; @@ -94,24 +103,25 @@ int ioctl (int fd, unsigned long int request, ...) return v4l1_ioctl (fd, request, arg); } -ssize_t read(int fd, void* buffer, size_t n) +LIBV4L_PUBLIC ssize_t read(int fd, void* buffer, size_t n) { return v4l1_read (fd, buffer, n); } -void mmap(void *start, size_t length, int prot, int flags, int fd, +LIBV4L_PUBLIC void *mmap(void *start, size_t length, int prot, int flags, int fd, __off_t offset) { return v4l1_mmap(start, length, prot, flags, fd, offset); } -void mmap64(void *start, size_t length, int prot, int flags, int fd, +LIBV4L_PUBLIC void *mmap64(void *start, size_t length, int prot, int flags, int fd, __off64_t offset) { return v4l1_mmap(start, length, prot, flags, fd, offset); } -int munmap(void *start, size_t length) +LIBV4L_PUBLIC int munmap(void *start, size_t length) { return v4l1_munmap(start, length); } + diff --git a/v4l2-apps/lib/libv4l/libv4l2/Makefile b/v4l2-apps/lib/libv4l/libv4l2/Makefile index 1258e379b..648d27c0c 100644 --- a/v4l2-apps/lib/libv4l/libv4l2/Makefile +++ b/v4l2-apps/lib/libv4l/libv4l2/Makefile @@ -1,20 +1,25 @@ -CC = gcc -LD = gcc - -CPPFLAGS = -fPIC -I../include -I../../../../linux/include +override CPPFLAGS += -I../include -I../../../include -fvisibility=hidden CFLAGS := -g -O1 -CFLAGS += -Wall -W -Wno-unused -Wpointer-arith -Wstrict-prototypes +CFLAGS += -Wall -Wno-unused -Wpointer-arith -Wstrict-prototypes -Wmissing-prototypes -LDFLAGS = -shared +LIBS = -lpthread -V4L2_OBJS = libv4l2.o log.o ../libv4lconvert/libv4lconvert.so -V4L2_LIB = libv4l2.so +V4L2_OBJS = libv4l2.o log.o V4L2CONVERT = v4l2convert.so V4L2CONVERT_O = v4l2convert.o libv4l2.so -TARGETS = $(V4L2_LIB) $(V4L2CONVERT) +TARGETS = $(V4L2_LIB) libv4l2.pc INCLUDES = ../include/libv4l2.h +ifeq ($(LINKTYPE),static) +V4L2_LIB = libv4l2.a +else +V4L2_LIB = libv4l2.so +V4L2_OBJS += ../libv4lconvert/libv4lconvert.so +TARGETS += $(V4L2CONVERT) +override CPPFLAGS += -fPIC +endif + ifeq ($(LIB_RELEASE),) LIB_RELEASE = 0 endif @@ -33,23 +38,45 @@ $(V4L2_LIB): $(V4L2_OBJS) $(V4L2CONVERT): $(V4L2CONVERT_O) $(V4L2_LIB) +libv4l2.pc: + @echo prefix=$(PREFIX) > libv4l2.pc + @echo libdir=$(LIBDIR) >> libv4l2.pc + @echo >> libv4l2.pc + @echo 'Name: libv4l2' >> libv4l2.pc + @echo 'Description: v4l2 device access library' >> libv4l2.pc + @echo 'Version: '$(V4L2_LIB_VERSION) >> libv4l2.pc + @echo 'Requires: libv4lconvert' >> libv4l2.pc + @echo 'Libs: -L$${libdir} -lv4l2' >> libv4l2.pc + @echo 'Libs.private: -lpthread' >> libv4l2.pc + @echo 'Cflags: -I$${prefix}/include' >> libv4l2.pc + install: all mkdir -p $(DESTDIR)$(PREFIX)/include install -p -m 644 $(INCLUDES) $(DESTDIR)$(PREFIX)/include +ifeq ($(LINKTYPE),static) + mkdir -p $(DESTDIR)$(LIBDIR) + install -m 644 $(V4L2_LIB) $(DESTDIR)$(LIBDIR) +else mkdir -p $(DESTDIR)$(LIBDIR)/libv4l 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) +endif + mkdir -p $(DESTDIR)$(LIBDIR)/pkgconfig + install -m 644 libv4l2.pc $(DESTDIR)$(LIBDIR)/pkgconfig clean:: - rm -f *.so* *.o log *~ - rm -f *.d + rm -f *.a *.so* *.o *.d libv4l2.pc log *~ %.o: %.c $(CC) -c -MMD $(CPPFLAGS) $(CFLAGS) -o $@ $< %.so: - $(CC) $(LDFLAGS) -Wl,-soname,$@.$(LIB_RELEASE) -o $@.$(LIB_RELEASE) $^ + $(CC) -shared $(LDFLAGS) -Wl,-soname,$@.$(LIB_RELEASE) -o $@.$(LIB_RELEASE) $^ $(LIBS) ln -f -s $@.$(LIB_RELEASE) $@ + +%.a: + $(AR) cqs $@ $^ + diff --git a/v4l2-apps/lib/libv4l/libv4l2/libv4l2-priv.h b/v4l2-apps/lib/libv4l/libv4l2/libv4l2-priv.h index 203dcffaf..8724832e1 100644 --- a/v4l2-apps/lib/libv4l/libv4l2/libv4l2-priv.h +++ b/v4l2-apps/lib/libv4l/libv4l2/libv4l2-priv.h @@ -8,8 +8,8 @@ # # 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. +# 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 @@ -68,8 +68,6 @@ #define MIN(a,b) (((a)<(b))?(a):(b)) -enum v4l2_io { v4l2_io_none, v4l2_io_read, v4l2_io_mmap }; - struct v4l2_dev_info { int fd; int flags; @@ -81,20 +79,14 @@ struct v4l2_dev_info { pthread_mutex_t stream_lock; unsigned int no_frames; unsigned int nreadbuffers; - enum v4l2_io io; struct v4lconvert_data *convert; unsigned char *convert_mmap_buf; /* Frame bookkeeping is only done when in read or mmap-conversion mode */ unsigned char *frame_pointers[V4L2_MAX_NO_FRAMES]; int frame_sizes[V4L2_MAX_NO_FRAMES]; int frame_queued; /* 1 status bit per frame */ - /* mapping tracking of our fake (converting mmap) frame buffers, todo this - perfect we should use a map counter per frame, this is a good - approximation but there are scenarios thinkable where this doesn't work. - However no normal application not even a buggy one is likely to exhibit - the patterns needed to fail this somewhat simplified tracking */ - int frame_mapped; /* 1 status bit per frame */ - int frame_map_count; /* total number of maps of (fake) buffers combined */ + /* mapping tracking of our fake (converting mmap) frame buffers */ + unsigned char frame_map_count[V4L2_MAX_NO_FRAMES]; }; /* From log.c */ diff --git a/v4l2-apps/lib/libv4l/libv4l2/libv4l2.c b/v4l2-apps/lib/libv4l/libv4l2/libv4l2.c index 8dfcf9b71..7029f69c3 100644 --- a/v4l2-apps/lib/libv4l/libv4l2/libv4l2.c +++ b/v4l2-apps/lib/libv4l/libv4l2/libv4l2.c @@ -8,8 +8,8 @@ # # 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. +# 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 @@ -35,12 +35,12 @@ capture devices. 2) libv4l2 is the base of the v4l2convert.so wrapper lib, which is a .so which can be LD_PRELOAD-ed and the overrules the libc's open/close/etc, - and when opening /dev/videoX calls v4l2_open. Because we behave as the - regular counterpart when the fd is not known (instead of say throwing - an error), v4l2convert.so can simply call the v4l2_ prefixed function - for all wrapped functions (except for v4l2_open which will fail when not - called on a v4l2 device). This way the wrapper does not have to keep - track of which fd's are being handled by libv4l2, as libv4l2 already + and when opening /dev/videoX or /dev/v4l/ calls v4l2_open. Because we + behave as the regular counterpart when the fd is not known (instead of say + throwing an error), v4l2convert.so can simply call the v4l2_ prefixed + function for all wrapped functions (except for v4l2_open which will fail + when not called on a v4l2 device). This way the wrapper does not have to + keep track of which fd's are being handled by libv4l2, as libv4l2 already keeps track of this itself. This also means that libv4l2 may not use any of the regular functions @@ -72,7 +72,10 @@ /* Note these flags are stored together with the flags passed to v4l2_fd_open() in v4l2_dev_info's flags member, so care should be taken that the do not use the same bits! */ -#define V4L2_STREAMON 0x0100 +#define V4L2_STREAMON 0x0100 +#define V4L2_BUFFERS_REQUESTED_BY_READ 0x0200 +#define V4L2_STREAM_CONTROLLED_BY_READ 0x0400 +#define V4L2_SUPPORTS_READ 0x0800 #define V4L2_MMAP_OFFSET_MAGIC 0xABCDEF00u @@ -89,52 +92,45 @@ static int v4l2_request_read_buffers(int index) int result; struct v4l2_requestbuffers req; - /* No-op if already done */ - if (devices[index].no_frames) - return 0; - - /* Request buffers */ - req.count = devices[index].nreadbuffers; + /* Note we re-request the buffers if they are already requested as the format + and thus the needed buffersize may have changed. */ + req.count = (devices[index].no_frames)? devices[index].no_frames: + devices[index].nreadbuffers; req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; req.memory = V4L2_MEMORY_MMAP; - if ((result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_REQBUFS, &req))){ + if ((result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_REQBUFS, &req)) < 0){ int saved_err = errno; - V4L2_LOG_ERR("requesting buffers: %s\n", strerror(errno)); + V4L2_LOG_ERR("requesting %u buffers: %s\n", req.count, strerror(errno)); errno = saved_err; return result; } + if (!devices[index].no_frames && req.count) + devices[index].flags |= V4L2_BUFFERS_REQUESTED_BY_READ; + devices[index].no_frames = MIN(req.count, V4L2_MAX_NO_FRAMES); return 0; } -static int v4l2_unrequest_read_buffers(int index) +static void v4l2_unrequest_read_buffers(int index) { - int result; struct v4l2_requestbuffers req; - /* No-op of already done */ - if (devices[index].no_frames == 0) - return 0; + if (!(devices[index].flags & V4L2_BUFFERS_REQUESTED_BY_READ) || + devices[index].no_frames == 0) + return; - /* (Un)Request buffers */ + /* (Un)Request buffers, note not all driver support this, and those + who do not support it don't need it. */ req.count = 0; req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; req.memory = V4L2_MEMORY_MMAP; - if ((result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_REQBUFS, &req))) { - int saved_err = errno; - V4L2_LOG_ERR("unrequesting buffers: %s\n", strerror(errno)); - errno = saved_err; - return result; - } + if(syscall(SYS_ioctl, devices[index].fd, VIDIOC_REQBUFS, &req) < 0) + return; devices[index].no_frames = MIN(req.count, V4L2_MAX_NO_FRAMES); - if (devices[index].no_frames) { - V4L2_LOG_ERR("number of buffers > 0 after requesting 0 buffers\n"); - errno = EBUSY; - return -1; - } - return 0; + if (devices[index].no_frames == 0) + devices[index].flags &= ~V4L2_BUFFERS_REQUESTED_BY_READ; } static int v4l2_map_buffers(int index) @@ -159,7 +155,7 @@ static int v4l2_map_buffers(int index) } devices[index].frame_pointers[i] = (void *)syscall(SYS_mmap2, NULL, - (size_t)buf.length, PROT_READ, MAP_SHARED, devices[index].fd, + (size_t)buf.length, PROT_READ|PROT_WRITE, MAP_SHARED, devices[index].fd, (__off_t)(buf.m.offset >> MMAP2_PAGE_SHIFT)); if (devices[index].frame_pointers[i] == MAP_FAILED) { int saved_err = errno; @@ -241,6 +237,7 @@ static int v4l2_queue_read_buffer(int index, int buffer_index) if (devices[index].frame_queued & (1 << buffer_index)) return 0; + memset(&buf, 0, sizeof(buf)); buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; buf.index = buffer_index; @@ -255,23 +252,54 @@ static int v4l2_queue_read_buffer(int index, int buffer_index) return 0; } -static int v4l2_dequeue_read_buffer(int index, int *bytesused) +static int v4l2_dequeue_and_convert(int index, struct v4l2_buffer *buf, + unsigned char *dest, int dest_size) { - int result; - struct v4l2_buffer buf; + const int max_tries = 10; + int result, tries = max_tries; - buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - buf.memory = V4L2_MEMORY_MMAP; - if ((result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_DQBUF, &buf))) { - int saved_err = errno; - V4L2_LOG_ERR("dequeuing buf: %s\n", strerror(errno)); - errno = saved_err; + /* Make sure we have the real v4l2 buffers mapped */ + if ((result = v4l2_map_buffers(index))) return result; + + do { + if ((result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_DQBUF, buf))) { + int saved_err = errno; + V4L2_LOG_ERR("dequeuing buf: %s\n", strerror(errno)); + errno = saved_err; + return result; + } + + devices[index].frame_queued &= ~(1 << buf->index); + + result = v4lconvert_convert(devices[index].convert, + &devices[index].src_fmt, &devices[index].dest_fmt, + devices[index].frame_pointers[buf->index], + buf->bytesused, dest ? dest : (devices[index].convert_mmap_buf + + buf->index * V4L2_FRAME_BUF_SIZE), dest_size); + + if (result < 0) { + int saved_err = errno; + + if(errno == EAGAIN) + V4L2_LOG("warning error while converting frame data: %s\n", + v4lconvert_get_error_message(devices[index].convert)); + else + V4L2_LOG_ERR("converting / decoding frame data: %s\n", + v4lconvert_get_error_message(devices[index].convert)); + + v4l2_queue_read_buffer(index, buf->index); + errno = saved_err; + } + tries--; + } while (result < 0 && errno == EAGAIN && tries); + + if (result < 0 && errno == EAGAIN) { + V4L2_LOG_ERR("got %d consecutive frame decode errors, last error: %s\n", + max_tries, v4lconvert_get_error_message(devices[index].convert)); } - devices[index].frame_queued &= ~(1 << buf.index); - *bytesused = buf.bytesused; - return buf.index; + return result; } static int v4l2_queue_read_buffers(int index) @@ -310,6 +338,8 @@ static int v4l2_activate_read_stream(int index) if ((result = v4l2_queue_read_buffers(index))) return result; + devices[index].flags |= V4L2_STREAM_CONTROLLED_BY_READ; + return result = v4l2_streamon(index); } @@ -324,12 +354,48 @@ static int v4l2_deactivate_read_stream(int index) v4l2_unmap_buffers(index); - if ((result = v4l2_unrequest_read_buffers(index))) - return result; + v4l2_unrequest_read_buffers(index); + + devices[index].flags &= ~V4L2_STREAM_CONTROLLED_BY_READ; return 0; } +static int v4l2_buffers_mapped(int index) +{ + unsigned int i; + + if (devices[index].src_fmt.fmt.pix.pixelformat == + devices[index].dest_fmt.fmt.pix.pixelformat) { + /* Normal (no conversion) mode */ + struct v4l2_buffer buf; + + for (i = 0; i < devices[index].no_frames; i++) { + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; + buf.index = i; + if (syscall(SYS_ioctl, devices[index].fd, VIDIOC_QUERYBUF, &buf)) { + int saved_err = errno; + V4L2_LOG_ERR("querying buffer %u: %s\n", i, strerror(errno)); + errno = saved_err; + break; + } + if (buf.flags & V4L2_BUF_FLAG_MAPPED) + break; + } + } else { + /* Conversion mode */ + for (i = 0; i < devices[index].no_frames; i++) + if (devices[index].frame_map_count[i]) + break; + } + + if (i != devices[index].no_frames) + V4L2_LOG("v4l2_buffers_mapped(): buffers still mapped\n"); + + return i != devices[index].no_frames; +} + int v4l2_open (const char *file, int oflag, ...) { @@ -386,8 +452,10 @@ int v4l2_fd_open(int fd, int v4l2_flags) return -1; } - /* we only add functionality for video capture devices */ - if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) + /* we only add functionality for video capture devices, and we do not + handle devices which don't do mmap */ + if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) || + !(cap.capabilities & V4L2_CAP_STREAMING)) return fd; /* Get current cam format */ @@ -420,6 +488,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; devices[index].open_count = 1; devices[index].src_fmt = fmt; devices[index].dest_fmt = fmt; @@ -428,15 +498,13 @@ int v4l2_fd_open(int fd, int v4l2_flags) devices[index].no_frames = 0; devices[index].nreadbuffers = V4L2_DEFAULT_NREADBUFFERS; - devices[index].io = v4l2_io_none; devices[index].convert = convert; devices[index].convert_mmap_buf = MAP_FAILED; for (i = 0; i < V4L2_MAX_NO_FRAMES; i++) { devices[index].frame_pointers[i] = MAP_FAILED; + devices[index].frame_map_count[i] = 0; } devices[index].frame_queued = 0; - devices[index].frame_mapped = 0; - devices[index].frame_map_count = 0; if (index >= devices_used) devices_used = index + 1; @@ -487,11 +555,8 @@ int v4l2_close(int fd) v4l2_unmap_buffers(index); v4lconvert_destroy(devices[index].convert); if (devices[index].convert_mmap_buf != MAP_FAILED) { - if (devices[index].frame_mapped || devices[index].frame_map_count) - V4L2_LOG( - "v4l2 mmap buffers still mapped on close(), mask: %08x, count: %d\n", - (unsigned int)devices[index].frame_mapped, - devices[index].frame_map_count); + if (v4l2_buffers_mapped(index)) + V4L2_LOG_WARN("v4l2 mmap buffers still mapped on close()\n"); else syscall(SYS_munmap, devices[index].convert_mmap_buf, devices[index].no_frames * V4L2_FRAME_BUF_SIZE); @@ -526,6 +591,34 @@ int v4l2_dup(int fd) return fd; } +static int v4l2_check_buffer_change_ok(int index) +{ + v4l2_unmap_buffers(index); + + /* Check if the app itself still is using the stream */ + if (v4l2_buffers_mapped(index) || + (!(devices[index].flags & V4L2_STREAM_CONTROLLED_BY_READ) && + ((devices[index].flags & V4L2_STREAMON) || + devices[index].frame_queued))) { + V4L2_LOG("v4l2_check_buffer_change_ok(): stream busy\n"); + errno = EBUSY; + return -1; + } + + /* We may change from convert to non conversion mode and + v4l2_unrequest_read_buffers may change the no_frames, so free the + convert mmap buffer */ + syscall(SYS_munmap, devices[index].convert_mmap_buf, + devices[index].no_frames * V4L2_FRAME_BUF_SIZE); + devices[index].convert_mmap_buf = MAP_FAILED; + + if (devices[index].flags & V4L2_STREAM_CONTROLLED_BY_READ) { + V4L2_LOG("deactivating read-stream for settings change\n"); + return v4l2_deactivate_read_stream(index); + } + + return 0; +} int v4l2_ioctl (int fd, unsigned long int request, ...) { @@ -548,8 +641,12 @@ int v4l2_ioctl (int fd, unsigned long int request, ...) /* Is this a capture request and do we need to take the stream lock? */ switch (request) { + case VIDIOC_QUERYCAP: + is_capture_request = 1; + break; case VIDIOC_ENUM_FMT: - if (((struct v4l2_fmtdesc *)arg)->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + if (((struct v4l2_fmtdesc *)arg)->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && + (devices[index].flags & V4L2_ENABLE_ENUM_FMT_EMULATION)) is_capture_request = 1; break; case VIDIOC_TRY_FMT: @@ -598,11 +695,21 @@ int v4l2_ioctl (int fd, unsigned long int request, ...) if (stream_needs_locking) pthread_mutex_lock(&devices[index].stream_lock); - converting = devices[index].src_fmt.fmt.pix.pixelformat != - devices[index].dest_fmt.fmt.pix.pixelformat; - + converting = v4lconvert_needs_conversion(devices[index].convert, + &devices[index].src_fmt, &devices[index].dest_fmt); switch (request) { + case VIDIOC_QUERYCAP: + { + struct v4l2_capability *cap = arg; + + result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_QUERYCAP, cap); + if (result == 0) + /* We always support read() as we fake it using mmap mode */ + cap->capabilities |= V4L2_CAP_READWRITE; + } + break; + case VIDIOC_ENUM_FMT: result = v4lconvert_enum_fmt(devices[index].convert, arg); break; @@ -620,17 +727,6 @@ int v4l2_ioctl (int fd, unsigned long int request, ...) break; } - /* Don't allow changing the format when mmap-ed IO is active, we could - allow this to happen in certain special circumstances, but it is - best to consistently deny this so that application developers do not - go expect this to work, because in their test setup it happens to - work. This also keeps the code much saner. */ - if (devices[index].io == v4l2_io_mmap) { - errno = EBUSY; - result = -1; - break; - } - if (devices[index].flags & V4L2_DISABLE_CONVERSION) { result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_TRY_FMT, dest_fmt); @@ -651,16 +747,8 @@ int v4l2_ioctl (int fd, unsigned long int request, ...) break; } - if (devices[index].io == v4l2_io_read) { - V4L2_LOG("deactivating read-stream for format change\n"); - if ((result = v4l2_deactivate_read_stream(index))) { - /* Undo what we've done to leave things in a consisten state */ - if (v4l2_activate_read_stream(index)) - V4L2_LOG_ERR( - "reactivating stream after deactivate failure (AAIIEEEE)\n"); - break; - } - } + if ((result = v4l2_check_buffer_change_ok(index))) + break; result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_S_FMT, &src_fmt); if (result) { @@ -690,22 +778,6 @@ int v4l2_ioctl (int fd, unsigned long int request, ...) { struct v4l2_requestbuffers *req = arg; - /* Don't allow mixing read / mmap io, either we control the buffers - (read based io), or the app does */ - if (devices[index].io == v4l2_io_read) { - V4L2_LOG_ERR("to change from read io to mmap io open and close the device first!\n"); - errno = EBUSY; - result = -1; - break; - } - - /* Are any of our fake (convert_mmap_buf) buffers still mapped ? */ - if (devices[index].frame_mapped || devices[index].frame_map_count) { - errno = EBUSY; - result = -1; - break; - } - /* IMPROVEME (maybe?) add support for userptr's? */ if (req->memory != V4L2_MEMORY_MMAP) { errno = EINVAL; @@ -713,29 +785,20 @@ int v4l2_ioctl (int fd, unsigned long int request, ...) break; } + if ((result = v4l2_check_buffer_change_ok(index))) + break; + /* No more buffers then we can manage please */ if (req->count > V4L2_MAX_NO_FRAMES) req->count = V4L2_MAX_NO_FRAMES; - /* Stop stream and unmap our real mapping of the buffers - (only relevant when we're converting, otherwise a no-op) */ - v4l2_streamoff(index); - v4l2_unmap_buffers(index); - result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_REQBUFS, req); - if (result) + if (result < 0) break; + result = 0; /* some drivers return the number of buffers on success */ - /* If we got more frames then we can handle lie to the app */ - if (req->count > V4L2_MAX_NO_FRAMES) - req->count = V4L2_MAX_NO_FRAMES; - - /* Force reallocation of convert_mmap_buf to fit the new no_frames */ - syscall(SYS_munmap, devices[index].convert_mmap_buf, - devices[index].no_frames * V4L2_FRAME_BUF_SIZE); - devices[index].convert_mmap_buf = MAP_FAILED; - devices[index].no_frames = req->count; - devices[index].io = req->count? v4l2_io_mmap:v4l2_io_none; + devices[index].no_frames = MIN(req->count, V4L2_MAX_NO_FRAMES); + devices[index].flags &= ~V4L2_BUFFERS_REQUESTED_BY_READ; } break; @@ -743,6 +806,10 @@ int v4l2_ioctl (int fd, unsigned long int request, ...) { struct v4l2_buffer *buf = arg; + if (devices[index].flags & V4L2_STREAM_CONTROLLED_BY_READ) + if ((result = v4l2_deactivate_read_stream(index))) + break; + /* Do a real query even when converting to let the driver fill in things like buf->field */ result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_QUERYBUF, buf); @@ -751,10 +818,23 @@ int v4l2_ioctl (int fd, unsigned long int request, ...) buf->m.offset = V4L2_MMAP_OFFSET_MAGIC | buf->index; buf->length = V4L2_FRAME_BUF_SIZE; + if (devices[index].frame_map_count[buf->index]) + buf->flags |= V4L2_BUF_FLAG_MAPPED; + else + buf->flags &= ~V4L2_BUF_FLAG_MAPPED; } break; case VIDIOC_QBUF: + if (devices[index].flags & V4L2_STREAM_CONTROLLED_BY_READ) + if ((result = v4l2_deactivate_read_stream(index))) + break; + + /* With some drivers the buffers must be mapped before queuing */ + if (converting) + if ((result = v4l2_map_buffers(index))) + break; + result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_QBUF, arg); break; @@ -762,14 +842,19 @@ int v4l2_ioctl (int fd, unsigned long int request, ...) { struct v4l2_buffer *buf = arg; - result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_DQBUF, buf); - if (result) { - V4L2_LOG_ERR("dequeing buffer: %s\n", strerror(errno)); - break; - } + if (devices[index].flags & V4L2_STREAM_CONTROLLED_BY_READ) + if ((result = v4l2_deactivate_read_stream(index))) + break; - if (!converting) + if (!converting) { + result = syscall(SYS_ioctl, devices[index].fd, VIDIOC_DQBUF, buf); + if (result) { + int saved_err = errno; + V4L2_LOG_ERR("dequeuing buf: %s\n", strerror(errno)); + errno = saved_err; + } break; + } /* An application can do a DQBUF before mmap-ing in the buffer, but we need the buffer _now_ to write our converted data @@ -791,26 +876,17 @@ int v4l2_ioctl (int fd, unsigned long int request, ...) } } - /* Make sure we have the real v4l2 buffers mapped before trying to - read from them */ - if ((result = v4l2_map_buffers(index))) - break; - - result = v4lconvert_convert(devices[index].convert, - &devices[index].src_fmt, &devices[index].dest_fmt, - devices[index].frame_pointers[buf->index], - buf->bytesused, - devices[index].convert_mmap_buf + - buf->index * V4L2_FRAME_BUF_SIZE, - V4L2_FRAME_BUF_SIZE); - if (result < 0) { - V4L2_LOG_ERR("converting / decoding frame data: %s\n", - v4lconvert_get_error_message(devices[index].convert)); + result = v4l2_dequeue_and_convert(index, buf, 0, V4L2_FRAME_BUF_SIZE); + if (result < 0) break; - } buf->bytesused = result; + buf->m.offset = V4L2_MMAP_OFFSET_MAGIC | buf->index; buf->length = V4L2_FRAME_BUF_SIZE; + if (devices[index].frame_map_count[buf->index]) + buf->flags |= V4L2_BUF_FLAG_MAPPED; + else + buf->flags &= ~V4L2_BUF_FLAG_MAPPED; result = 0; } @@ -818,11 +894,9 @@ int v4l2_ioctl (int fd, unsigned long int request, ...) case VIDIOC_STREAMON: case VIDIOC_STREAMOFF: - if (devices[index].io != v4l2_io_mmap) { - errno = EINVAL; - result = -1; - break; - } + if (devices[index].flags & V4L2_STREAM_CONTROLLED_BY_READ) + if ((result = v4l2_deactivate_read_stream(index))) + break; if (request == VIDIOC_STREAMON) result = v4l2_streamon(index); @@ -845,42 +919,43 @@ int v4l2_ioctl (int fd, unsigned long int request, ...) } -ssize_t v4l2_read (int fd, void* buffer, size_t n) +ssize_t v4l2_read (int fd, void* dest, size_t n) { ssize_t result; - int index, bytesused = 0, frame_index; + int index; + struct v4l2_buffer buf; if ((index = v4l2_get_index(fd)) == -1) - return syscall(SYS_read, fd, buffer, n); + return syscall(SYS_read, fd, dest, n); pthread_mutex_lock(&devices[index].stream_lock); - if (devices[index].io == v4l2_io_mmap) { - V4L2_LOG_ERR("to change from mmap io to read io first do request_buffers with a count of 0\n"); - errno = EBUSY; - result = -1; + /* When not converting and the device supports read let the kernel handle + it */ + if ((devices[index].flags & V4L2_SUPPORTS_READ) && + !v4lconvert_needs_conversion(devices[index].convert, + &devices[index].src_fmt, &devices[index].dest_fmt)) { + result = syscall(SYS_read, fd, dest, n); goto leave; } - devices[index].io = v4l2_io_read; - - if ((result = v4l2_activate_read_stream(index))) - goto leave; - if ((frame_index = v4l2_dequeue_read_buffer(index, &bytesused)) < 0) { - result = -1; - 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; + } + if ((result = v4l2_activate_read_stream(index))) + goto leave; } - result = v4lconvert_convert(devices[index].convert, - &devices[index].src_fmt, &devices[index].dest_fmt, - devices[index].frame_pointers[frame_index], bytesused, - buffer, n); - - v4l2_queue_read_buffer(index, frame_index); + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; + result = v4l2_dequeue_and_convert(index, &buf, dest, n); - if (result < 0) - V4L2_LOG_ERR("converting / decoding frame data: %s\n", - v4lconvert_get_error_message(devices[index].convert)); + if (result >= 0) + v4l2_queue_read_buffer(index, buf.index); leave: pthread_mutex_unlock(&devices[index].stream_lock); @@ -917,10 +992,9 @@ void *v4l2_mmap(void *start, size_t length, int prot, int flags, int fd, buffer_index = offset & 0xff; if (buffer_index >= devices[index].no_frames || - devices[index].io != v4l2_io_mmap || /* Got magic offset and not converting ?? */ - devices[index].src_fmt.fmt.pix.pixelformat == - devices[index].dest_fmt.fmt.pix.pixelformat) { + !v4lconvert_needs_conversion(devices[index].convert, + &devices[index].src_fmt, &devices[index].dest_fmt)) { errno = EINVAL; result = MAP_FAILED; goto leave; @@ -943,8 +1017,7 @@ void *v4l2_mmap(void *start, size_t length, int prot, int flags, int fd, } } - devices[index].frame_mapped |= 1 << buffer_index; - devices[index].frame_map_count++; + devices[index].frame_map_count[buffer_index]++; result = devices[index].convert_mmap_buf + buffer_index * V4L2_FRAME_BUF_SIZE; @@ -985,9 +1058,8 @@ int v4l2_munmap(void *_start, size_t length) start >= devices[index].convert_mmap_buf && (start - devices[index].convert_mmap_buf) % length == 0 && buffer_index < devices[index].no_frames) { - devices[index].frame_mapped &= ~(1 << buffer_index); - if (devices[index].frame_map_count > 0) - devices[index].frame_map_count--; + if (devices[index].frame_map_count[buffer_index] > 0) + devices[index].frame_map_count[buffer_index]--; unmapped = 1; } diff --git a/v4l2-apps/lib/libv4l/libv4l2/log.c b/v4l2-apps/lib/libv4l/libv4l2/log.c index 982a185c6..6237d55ec 100644 --- a/v4l2-apps/lib/libv4l/libv4l2/log.c +++ b/v4l2-apps/lib/libv4l/libv4l2/log.c @@ -8,8 +8,8 @@ # # 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. +# 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 @@ -25,6 +25,8 @@ #include <asm/types.h> /* end broken header workaround includes */ #include <linux/videodev2.h> +#include "libv4l2.h" +#include "libv4l2-priv.h" #define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0])) @@ -116,7 +118,7 @@ void v4l2_log_ioctl(unsigned long int request, void *arg, int result) int pixfmt = fmt->fmt.pix.pixelformat; if (fmt->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - fprintf(v4l2_log_file, " pixelformat: %c%c%c%c %dx%d\n", + fprintf(v4l2_log_file, " pixelformat: %c%c%c%c %ux%u\n", pixfmt & 0xff, (pixfmt >> 8) & 0xff, (pixfmt >> 16) & 0xff, @@ -131,6 +133,14 @@ void v4l2_log_ioctl(unsigned long int request, void *arg, int result) } } break; + case VIDIOC_REQBUFS: + { + struct v4l2_requestbuffers *req = arg; + + fprintf(v4l2_log_file, " count: %u type: %d memory: %d\n", + req->count, (int)req->type, (int)req->memory); + } + break; } fprintf(v4l2_log_file, "result == %d\n", result); diff --git a/v4l2-apps/lib/libv4l/libv4l2/v4l2convert.c b/v4l2-apps/lib/libv4l/libv4l2/v4l2convert.c index 7db1ca6d6..e8756f338 100644 --- a/v4l2-apps/lib/libv4l/libv4l2/v4l2convert.c +++ b/v4l2-apps/lib/libv4l/libv4l2/v4l2convert.c @@ -12,8 +12,8 @@ # # 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. +# 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 @@ -27,6 +27,8 @@ #include <syscall.h> #include <fcntl.h> #include <string.h> +#include <sys/ioctl.h> +#include <sys/mman.h> /* These headers are not needed by us, but by linux/videodev2.h, which is broken on some systems and doesn't include them itself :( */ #include <sys/time.h> @@ -41,7 +43,13 @@ #error open/read/mmap is a prepocessor macro !! #endif -int open (const char *file, int oflag, ...) +#if __GNUC__ >= 4 +#define LIBV4L_PUBLIC __attribute__ ((visibility("default"))) +#else +#define LIBV4L_PUBLIC +#endif + +LIBV4L_PUBLIC int open (const char *file, int oflag, ...) { int fd; struct v4l2_capability cap; @@ -66,7 +74,7 @@ int open (const char *file, int oflag, ...) return fd; /* check if we're opening a video4linux2 device */ - if (strncmp(file, "/dev/video", 10)) + if (strncmp(file, "/dev/video", 10) && strncmp(file, "/dev/v4l/", 9)) return fd; /* check that this is an v4l2 device, libv4l2 only supports v4l2 devices */ @@ -84,7 +92,7 @@ int open (const char *file, int oflag, ...) return fd; } -int open64(const char *file, int oflag, ...) +LIBV4L_PUBLIC int open64(const char *file, int oflag, ...) { int fd; @@ -107,17 +115,17 @@ int open64(const char *file, int oflag, ...) return fd; } -int close(int fd) +LIBV4L_PUBLIC int close(int fd) { return v4l2_close(fd); } -int dup(int fd) +LIBV4L_PUBLIC int dup(int fd) { return v4l2_dup(fd); } -int ioctl (int fd, unsigned long int request, ...) +LIBV4L_PUBLIC int ioctl (int fd, unsigned long int request, ...) { void *arg; va_list ap; @@ -129,24 +137,25 @@ int ioctl (int fd, unsigned long int request, ...) return v4l2_ioctl (fd, request, arg); } -ssize_t read (int fd, void* buffer, size_t n) +LIBV4L_PUBLIC ssize_t read (int fd, void* buffer, size_t n) { return v4l2_read (fd, buffer, n); } -void mmap(void *start, size_t length, int prot, int flags, int fd, +LIBV4L_PUBLIC void *mmap(void *start, size_t length, int prot, int flags, int fd, __off_t offset) { return v4l2_mmap(start, length, prot, flags, fd, offset); } -void mmap64(void *start, size_t length, int prot, int flags, int fd, +LIBV4L_PUBLIC void *mmap64(void *start, size_t length, int prot, int flags, int fd, __off64_t offset) { return v4l2_mmap(start, length, prot, flags, fd, offset); } -int munmap(void *start, size_t length) +LIBV4L_PUBLIC int munmap(void *start, size_t length) { return v4l2_munmap(start, length); } + diff --git a/v4l2-apps/lib/libv4l/libv4lconvert/Makefile b/v4l2-apps/lib/libv4l/libv4lconvert/Makefile index 38071de94..641d19d6e 100644 --- a/v4l2-apps/lib/libv4l/libv4lconvert/Makefile +++ b/v4l2-apps/lib/libv4l/libv4lconvert/Makefile @@ -1,17 +1,18 @@ -CC = gcc -LD = gcc - -CPPFLAGS = -fPIC -I../include -I../../../../linux/include +override CPPFLAGS += -I../include -I../../../include -fvisibility=hidden CFLAGS := -g -O1 -CFLAGS += -Wall -W -Wno-unused -Wpointer-arith -Wstrict-prototypes - -LDFLAGS = -shared +CFLAGS += -Wall -Wno-unused -Wpointer-arith -Wstrict-prototypes -Wmissing-prototypes +ifeq ($(LINKTYPE),static) +CONVERT_LIB = libv4lconvert.a +else CONVERT_LIB = libv4lconvert.so -CONVERT_OBJS = libv4lconvert.o tinyjpeg.o sn9c10x.o pac207.o \ +override CPPFLAGS += -fPIC +endif + +CONVERT_OBJS = libv4lconvert.o tinyjpeg.o sn9c10x.o pac207.o flip.o \ jidctflt.o spca561-decompress.o rgbyuv.o spca501.o bayer.o -TARGETS = $(CONVERT_LIB) +TARGETS = $(CONVERT_LIB) libv4lconvert.pc INCLUDES = ../include/libv4lconvert.h ifeq ($(LIB_RELEASE),) @@ -30,21 +31,40 @@ all: $(TARGETS) $(CONVERT_LIB): $(CONVERT_OBJS) +libv4lconvert.pc: + @echo prefix=$(PREFIX) > libv4lconvert.pc + @echo libdir=$(LIBDIR) >> libv4lconvert.pc + @echo >> libv4lconvert.pc + @echo 'Name: libv4lconvert' >> libv4lconvert.pc + @echo 'Description: v4l format conversion library' >> libv4lconvert.pc + @echo 'Version: '$(V4L2_LIB_VERSION) >> libv4lconvert.pc + @echo 'Libs: -L$${libdir} -lv4lconvert' >> libv4lconvert.pc + @echo 'Cflags: -I$${prefix}/include' >> libv4lconvert.pc + install: all mkdir -p $(DESTDIR)$(PREFIX)/include install -p -m 644 $(INCLUDES) $(DESTDIR)$(PREFIX)/include mkdir -p $(DESTDIR)$(LIBDIR) +ifeq ($(LINKTYPE),static) + mkdir -p $(DESTDIR)$(LIBDIR) + install -m 644 $(CONVERT_LIB) $(DESTDIR)$(LIBDIR) +else install -m 755 $(CONVERT_LIB).$(LIB_RELEASE) $(DESTDIR)$(LIBDIR) cd $(DESTDIR)$(LIBDIR) && \ ln -f -s $(CONVERT_LIB).$(LIB_RELEASE) $(CONVERT_LIB) +endif + mkdir -p $(DESTDIR)$(LIBDIR)/pkgconfig + install -m 644 libv4lconvert.pc $(DESTDIR)$(LIBDIR)/pkgconfig clean:: - rm -f *.so* *.o log *~ - rm -f *.d + rm -f *.a *.so* *.o *.d libv4lconvert.pc log *~ %.o: %.c $(CC) -c -MMD $(CPPFLAGS) $(CFLAGS) -o $@ $< %.so: - $(CC) $(LDFLAGS) -Wl,-soname,$@.$(LIB_RELEASE) -o $@.$(LIB_RELEASE) $^ + $(CC) -shared $(LDFLAGS) -Wl,-soname,$@.$(LIB_RELEASE) -o $@.$(LIB_RELEASE) $^ ln -f -s $@.$(LIB_RELEASE) $@ + +%.a: + $(AR) cqs $@ $^ diff --git a/v4l2-apps/lib/libv4l/libv4lconvert/bayer.c b/v4l2-apps/lib/libv4l/libv4lconvert/bayer.c index 166c13011..ca7bb486f 100644 --- a/v4l2-apps/lib/libv4l/libv4lconvert/bayer.c +++ b/v4l2-apps/lib/libv4l/libv4lconvert/bayer.c @@ -163,14 +163,10 @@ static void v4lconvert_border_bayer_line_to_bgr24( } /* From libdc1394, which on turn was based on OpenCV's Bayer decoding */ -void v4lconvert_bayer_to_bgr24(const unsigned char *bayer, - unsigned char *bgr, int width, int height, unsigned int pixfmt) +static void bayer_to_rgbbgr24(const unsigned char *bayer, + unsigned char *bgr, int width, int height, unsigned int pixfmt, + int start_with_green, int blue_line) { - int blue_line = pixfmt == V4L2_PIX_FMT_SBGGR8 - || pixfmt == V4L2_PIX_FMT_SGBRG8; - int start_with_green = pixfmt == V4L2_PIX_FMT_SGBRG8 - || pixfmt == V4L2_PIX_FMT_SGRBG8; - /* render the first line */ v4lconvert_border_bayer_line_to_bgr24(bayer, bayer + width, bgr, width, start_with_green, blue_line); @@ -317,6 +313,26 @@ void v4lconvert_bayer_to_bgr24(const unsigned char *bayer, !start_with_green, !blue_line); } +void v4lconvert_bayer_to_rgb24(const unsigned char *bayer, + unsigned char *bgr, int width, int height, unsigned int pixfmt) +{ + bayer_to_rgbbgr24(bayer, bgr, width, height, pixfmt, + pixfmt == V4L2_PIX_FMT_SGBRG8 /* start with green */ + || pixfmt == V4L2_PIX_FMT_SGRBG8, + pixfmt != V4L2_PIX_FMT_SBGGR8 /* blue line */ + && pixfmt != V4L2_PIX_FMT_SGBRG8); +} + +void v4lconvert_bayer_to_bgr24(const unsigned char *bayer, + unsigned char *bgr, int width, int height, unsigned int pixfmt) +{ + bayer_to_rgbbgr24(bayer, bgr, width, height, pixfmt, + pixfmt == V4L2_PIX_FMT_SGBRG8 /* start with green */ + || pixfmt == V4L2_PIX_FMT_SGRBG8, + pixfmt == V4L2_PIX_FMT_SBGGR8 /* blue line */ + || pixfmt == V4L2_PIX_FMT_SGBRG8); +} + static void v4lconvert_border_bayer_line_to_y( const unsigned char* bayer, const unsigned char* adjacent_bayer, unsigned char *y, int width, int start_with_green, int blue_line) diff --git a/v4l2-apps/lib/libv4l/libv4lconvert/flip.c b/v4l2-apps/lib/libv4l/libv4lconvert/flip.c new file mode 100644 index 000000000..cd3468a89 --- /dev/null +++ b/v4l2-apps/lib/libv4l/libv4lconvert/flip.c @@ -0,0 +1,107 @@ +/* + +# RGB / YUV flip/rotate routines + +# (C) 2008 Hans de Goede <j.w.r.degoede@hhs.nl> + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#include "libv4lconvert-priv.h" + +void v4lconvert_rotate180_rgbbgr24(const unsigned char *src, unsigned char *dst, + int width, int height) +{ + int i; + + src += 3 * width * height - 3; + + for (i = 0; i < width * height; i++) { + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[2]; + dst += 3; + src -= 3; + } +} + +void v4lconvert_rotate180_yuv420(const unsigned char *src, unsigned char *dst, + int width, int height) +{ + int i; + + /* First flip x and y of the Y plane */ + src += width * height - 1; + for (i = 0; i < width * height; i++) + *dst++ = *src--; + + /* Now flip the U plane */ + src += width * height * 5 / 4; + for (i = 0; i < width * height / 4; i++) + *dst++ = *src--; + + /* Last flip the V plane */ + src += width * height / 2; + for (i = 0; i < width * height / 4; i++) + *dst++ = *src--; +} + +void v4lconvert_rotate90_rgbbgr24(const unsigned char *src, unsigned char *dst, + int destwidth, int destheight) +{ + int x, y; +#define srcwidth destheight +#define srcheight destwidth + + for (y = 0; y < destheight; y++) + for (x = 0; x < destwidth; x++) { + int offset = ((srcheight - x - 1) * srcwidth + y) * 3; + *dst++ = src[offset++]; + *dst++ = src[offset++]; + *dst++ = src[offset]; + } +} + +void v4lconvert_rotate90_yuv420(const unsigned char *src, unsigned char *dst, + int destwidth, int destheight) +{ + int x, y; + + /* Y-plane */ + for (y = 0; y < destheight; y++) + for (x = 0; x < destwidth; x++) { + int offset = (srcheight - x - 1) * srcwidth + y; + *dst++ = src[offset]; + } + + /* U-plane */ + src += srcwidth * srcheight; + destwidth /= 2; + destheight /= 2; + for (y = 0; y < destheight; y++) + for (x = 0; x < destwidth; x++) { + int offset = (srcheight - x - 1) * srcwidth + y; + *dst++ = src[offset]; + } + + /* V-plane */ + src += srcwidth * srcheight; + for (y = 0; y < destheight; y++) + for (x = 0; x < destwidth; x++) { + int offset = (srcheight - x - 1) * srcwidth + y; + *dst++ = src[offset]; + } +} diff --git a/v4l2-apps/lib/libv4l/libv4lconvert/jidctflt.c b/v4l2-apps/lib/libv4l/libv4lconvert/jidctflt.c index ba70309c9..532abc7ea 100644 --- a/v4l2-apps/lib/libv4l/libv4lconvert/jidctflt.c +++ b/v4l2-apps/lib/libv4l/libv4lconvert/jidctflt.c @@ -80,7 +80,7 @@ #define DEQUANTIZE(coef,quantval) (((FAST_FLOAT) (coef)) * (quantval)) -#if defined(__GNUC__) && (defined(__i686__)) // || defined(__x86_64__)) +#if defined(__GNUC__) && (defined(__i686__) || defined(__x86_64__)) static inline unsigned char descale_and_clamp(int x, int shift) { @@ -92,7 +92,7 @@ static inline unsigned char descale_and_clamp(int x, int shift) "\tcmpl %4,%1\n" "\tcmovg %4,%1\n" : "=r"(x) - : "0"(x), "Ic"((unsigned char)shift), "ir"(1UL<<(shift-1)), "r" (0xff), "r" (0) + : "0"(x), "Ic"((unsigned char)shift), "ir"(1U<<(shift-1)), "r" (0xff), "r" (0) ); return x; } diff --git a/v4l2-apps/lib/libv4l/libv4lconvert/libv4lconvert-priv.h b/v4l2-apps/lib/libv4l/libv4lconvert/libv4lconvert-priv.h index badb89d62..915c33283 100644 --- a/v4l2-apps/lib/libv4l/libv4lconvert/libv4lconvert-priv.h +++ b/v4l2-apps/lib/libv4l/libv4lconvert/libv4lconvert-priv.h @@ -9,7 +9,7 @@ # 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. +# 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 @@ -24,7 +24,15 @@ #include "tinyjpeg.h" #ifndef V4L2_PIX_FMT_SPCA501 -#define V4L2_PIX_FMT_SPCA501 v4l2_fourcc('S','5','0','1') +#define V4L2_PIX_FMT_SPCA501 v4l2_fourcc('S','5','0','1') /* YUYV per line */ +#endif + +#ifndef V4L2_PIX_FMT_SPCA505 +#define V4L2_PIX_FMT_SPCA505 v4l2_fourcc('S','5','0','5') /* YYUV per line */ +#endif + +#ifndef V4L2_PIX_FMT_SPCA508 +#define V4L2_PIX_FMT_SPCA508 v4l2_fourcc('S','5','0','8') /* YUVY per line */ #endif #ifndef V4L2_PIX_FMT_SPCA561 @@ -35,6 +43,10 @@ #define V4L2_PIX_FMT_PAC207 v4l2_fourcc('P','2','0','7') #endif +#ifndef V4L2_PIX_FMT_PJPG +#define V4L2_PIX_FMT_PJPG v4l2_fourcc('P', 'J', 'P', 'G') +#endif + #ifndef V4L2_PIX_FMT_SGBRG8 #define V4L2_PIX_FMT_SGBRG8 v4l2_fourcc('G','B','R','G') #endif @@ -53,23 +65,38 @@ snprintf(data->error_msg, V4LCONVERT_ERROR_MSG_SIZE, \ "v4l-convert: error " __VA_ARGS__) +#define V4LCONVERT_UPSIDE_DOWN 0x01 struct v4lconvert_data { int fd; + int flags; /* bitfield */ int supported_src_formats; /* bitfield */ unsigned int no_formats; char error_msg[V4LCONVERT_ERROR_MSG_SIZE]; struct jdec_private *jdec; }; +struct v4lconvert_flags_info { + const char *card; + int flags; +}; + +void v4lconvert_yuv420_to_rgb24(const unsigned char *src, unsigned char *dst, + int width, int height); void v4lconvert_yuv420_to_bgr24(const unsigned char *src, unsigned char *dst, int width, int height); +void v4lconvert_swap_rgb(const unsigned char *src, unsigned char *dst, + int width, int height); + void v4lconvert_spca501_to_yuv420(const unsigned char *src, unsigned char *dst, int width, int height); -void v4lconvert_spca501_to_bgr24(const unsigned char *src, unsigned char *dst, +void v4lconvert_spca505_to_yuv420(const unsigned char *src, unsigned char *dst, + int width, int height); + +void v4lconvert_spca508_to_yuv420(const unsigned char *src, unsigned char *dst, int width, int height); void v4lconvert_decode_spca561(const unsigned char *src, unsigned char *dst, @@ -81,10 +108,25 @@ void v4lconvert_decode_sn9c10x(const unsigned char *src, unsigned char *dst, void v4lconvert_decode_pac207(const unsigned char *src, unsigned char *dst, int width, int height); +void v4lconvert_bayer_to_rgb24(const unsigned char *bayer, + unsigned char *rgb, int width, int height, unsigned int pixfmt); + void v4lconvert_bayer_to_bgr24(const unsigned char *bayer, unsigned char *rgb, int width, int height, unsigned int pixfmt); void v4lconvert_bayer_to_yuv420(const unsigned char *bayer, unsigned char *yuv, int width, int height, unsigned int pixfmt); +void v4lconvert_rotate90_rgbbgr24(const unsigned char *src, unsigned char *dst, + int destwidth, int destheight); + +void v4lconvert_rotate90_yuv420(const unsigned char *src, unsigned char *dst, + int destwidth, int destheight); + +void v4lconvert_rotate180_rgbbgr24(const unsigned char *src, unsigned char *dst, + int width, int height); + +void v4lconvert_rotate180_yuv420(const unsigned char *src, unsigned char *dst, + int width, int height); + #endif diff --git a/v4l2-apps/lib/libv4l/libv4lconvert/libv4lconvert.c b/v4l2-apps/lib/libv4l/libv4lconvert/libv4lconvert.c index 6c6cb693d..4b48bfac4 100644 --- a/v4l2-apps/lib/libv4l/libv4lconvert/libv4lconvert.c +++ b/v4l2-apps/lib/libv4l/libv4lconvert/libv4lconvert.c @@ -9,7 +9,7 @@ # 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. +# 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 @@ -30,6 +30,7 @@ /* Note for proper functioning of v4lconvert_enum_fmt the first entries in supported_src_pixfmts must match with the entries in supported_dst_pixfmts */ #define SUPPORTED_DST_PIXFMTS \ + V4L2_PIX_FMT_RGB24, \ V4L2_PIX_FMT_BGR24, \ V4L2_PIX_FMT_YUV420 @@ -42,20 +43,31 @@ static const unsigned int supported_src_pixfmts[] = { V4L2_PIX_FMT_SGRBG8, V4L2_PIX_FMT_SRGGB8, V4L2_PIX_FMT_SPCA501, + V4L2_PIX_FMT_SPCA505, + V4L2_PIX_FMT_SPCA508, V4L2_PIX_FMT_SPCA561, V4L2_PIX_FMT_SN9C10X, V4L2_PIX_FMT_PAC207, + V4L2_PIX_FMT_PJPG, }; static const unsigned int supported_dst_pixfmts[] = { SUPPORTED_DST_PIXFMTS }; +/* List of cams which need special flags */ +static const struct v4lconvert_flags_info v4lconvert_flags[] = { + { "USB Camera (0471:0325)", V4LCONVERT_UPSIDE_DOWN }, /* SPC200NC */ + { "USB Camera (0471:0326)", V4LCONVERT_UPSIDE_DOWN }, /* SPC300NC */ + { "SPC 200NC ", V4LCONVERT_UPSIDE_DOWN }, + { "SPC 300NC ", V4LCONVERT_UPSIDE_DOWN }, +}; struct v4lconvert_data *v4lconvert_create(int fd) { int i, j; struct v4lconvert_data *data = calloc(1, sizeof(struct v4lconvert_data)); + struct v4l2_capability cap; if (!data) return NULL; @@ -81,6 +93,15 @@ struct v4lconvert_data *v4lconvert_create(int fd) data->no_formats = i; + /* Check if this cam has any special flags */ + if (syscall(SYS_ioctl, fd, VIDIOC_QUERYCAP, &cap) == 0) { + for (i = 0; i < ARRAY_SIZE(v4lconvert_flags); i++) + if (!strcmp((const char *)v4lconvert_flags[i].card, (char *)cap.card)) { + data->flags = v4lconvert_flags[i].flags; + break; + } + } + return data; } @@ -168,7 +189,9 @@ int v4lconvert_try_format(struct v4lconvert_data *data, (int)dest_fmt->fmt.pix.height); unsigned int size_diff = size_x_diff * size_x_diff + size_y_diff * size_y_diff; - if (size_diff < closest_fmt_size_diff) { + if (size_diff < closest_fmt_size_diff || + (size_diff == closest_fmt_size_diff && + try_fmt.fmt.pix.pixelformat == desired_pixfmt)) { closest_fmt_size_diff = size_diff; closest_fmt = try_fmt; } @@ -189,6 +212,7 @@ int v4lconvert_try_format(struct v4lconvert_data *data, if (closest_fmt.fmt.pix.pixelformat != desired_pixfmt) { dest_fmt->fmt.pix.pixelformat = desired_pixfmt; switch (dest_fmt->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_RGB24: case V4L2_PIX_FMT_BGR24: dest_fmt->fmt.pix.bytesperline = dest_fmt->fmt.pix.width * 3; dest_fmt->fmt.pix.sizeimage = dest_fmt->fmt.pix.width * @@ -208,17 +232,42 @@ int v4lconvert_try_format(struct v4lconvert_data *data, return 0; } +/* Is conversion necessary ? */ +int v4lconvert_needs_conversion(struct v4lconvert_data *data, + const struct v4l2_format *src_fmt, /* in */ + const struct v4l2_format *dest_fmt) /* in */ +{ + int i; + + if(memcmp(src_fmt, dest_fmt, sizeof(*src_fmt))) + return 1; /* Formats differ */ + + if (!(data->flags & V4LCONVERT_UPSIDE_DOWN)) + return 0; /* Formats identical and we don't need flip */ + + /* Formats are identical, but we need flip, do we support the dest_fmt? */ + for (i = 0; i < ARRAY_SIZE(supported_dst_pixfmts); i++) + if (supported_dst_pixfmts[i] == dest_fmt->fmt.pix.pixelformat) + break; + + if (i == ARRAY_SIZE(supported_dst_pixfmts)) + return 0; /* Needs flip but we cannot do it :( */ + else + return 1; /* Needs flip and thus conversion */ +} + int v4lconvert_convert(struct v4lconvert_data *data, const struct v4l2_format *src_fmt, /* in */ const struct v4l2_format *dest_fmt, /* in */ - unsigned char *src, int src_size, unsigned char *dest, int dest_size) + unsigned char *src, int src_size, unsigned char *_dest, int dest_size) { unsigned int header_width, header_height; - int result, needed; + int result, needed, rotate = 0, jpeg_flags = TINYJPEG_FLAGS_MJPEG_TABLE; unsigned char *components[3]; + unsigned char *dest = _dest; /* Special case when no conversion is needed */ - if(!memcmp(src_fmt, dest_fmt, sizeof(*src_fmt))) { + if (!v4lconvert_needs_conversion(data, src_fmt, dest_fmt)) { int to_copy = MIN(dest_size, src_size); memcpy(dest, src, to_copy); return to_copy; @@ -226,6 +275,7 @@ int v4lconvert_convert(struct v4lconvert_data *data, /* sanity check, is the dest buffer large enough? */ switch (dest_fmt->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_RGB24: case V4L2_PIX_FMT_BGR24: needed = dest_fmt->fmt.pix.width * dest_fmt->fmt.pix.height * 3; break; @@ -244,7 +294,15 @@ int v4lconvert_convert(struct v4lconvert_data *data, return -1; } + if (data->flags & V4LCONVERT_UPSIDE_DOWN) { + rotate = 180; + dest = alloca(needed); + } + switch (src_fmt->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_PJPG: + jpeg_flags |= TINYJPEG_FLAGS_PIXART_JPEG; + /* Fall through */ case V4L2_PIX_FMT_MJPEG: case V4L2_PIX_FMT_JPEG: if (!data->jdec) { @@ -255,9 +313,7 @@ int v4lconvert_convert(struct v4lconvert_data *data, return -1; } } - tinyjpeg_set_flags(data->jdec, - (src_fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG)? - TINYJPEG_FLAGS_MJPEG_TABLE : 0); + tinyjpeg_set_flags(data->jdec, jpeg_flags); if (tinyjpeg_parse_header(data->jdec, src, src_size)) { V4LCONVERT_ERR("parsing JPEG header: %s\n", tinyjpeg_get_errorstring(data->jdec)); @@ -266,13 +322,22 @@ int v4lconvert_convert(struct v4lconvert_data *data, } tinyjpeg_get_size(data->jdec, &header_width, &header_height); - if (header_width != dest_fmt->fmt.pix.width || header_height != dest_fmt->fmt.pix.height) { - V4LCONVERT_ERR("unexpected width / height in JPEG header\n"); - V4LCONVERT_ERR("expected: %dx%d, header: %ux%u\n", - dest_fmt->fmt.pix.width, dest_fmt->fmt.pix.height, - header_width, header_height); - errno = EIO; - return -1; + if (header_width != dest_fmt->fmt.pix.width || + header_height != dest_fmt->fmt.pix.height) { + /* Check for (pixart) rotated JPEG */ + if (header_width == dest_fmt->fmt.pix.height || + header_height == dest_fmt->fmt.pix.width) { + if (!rotate) + dest = alloca(needed); + rotate += 90; + } else { + V4LCONVERT_ERR("unexpected width / height in JPEG header" + "expected: %ux%u, header: %ux%u\n", + dest_fmt->fmt.pix.width, dest_fmt->fmt.pix.height, + header_width, header_height); + errno = EIO; + return -1; + } } components[0] = dest; @@ -281,43 +346,98 @@ int v4lconvert_convert(struct v4lconvert_data *data, components[2] = components[1] + (dest_fmt->fmt.pix.width * dest_fmt->fmt.pix.height) / 4; - if (dest_fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_BGR24) { + switch (dest_fmt->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_RGB24: + tinyjpeg_set_components(data->jdec, components, 1); + result = tinyjpeg_decode(data->jdec, TINYJPEG_FMT_RGB24); + break; + case V4L2_PIX_FMT_BGR24: tinyjpeg_set_components(data->jdec, components, 1); result = tinyjpeg_decode(data->jdec, TINYJPEG_FMT_BGR24); - } else { + break; + default: tinyjpeg_set_components(data->jdec, components, 3); result = tinyjpeg_decode(data->jdec, TINYJPEG_FMT_YUV420P); + break; } - /* If the JPEG header checked out ok and we get an error during actual - decompression, log the error, but don't return an errorcode to the - application, so that the user gets what we managed to decompress */ - if (result) - fprintf(stderr, "libv4lconvert: Error decompressing JPEG: %s", - tinyjpeg_get_errorstring(data->jdec)); - + if (result) { + /* Pixart webcam's seem to regulary generate corrupt frames, which + are best thrown away to avoid flashes in the video stream. Tell + the upper layer this is an intermediate fault and it should try + again with a new buffer by setting errno to EAGAIN */ + if (src_fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_PJPG) { + V4LCONVERT_ERR("Error decompressing JPEG: %s", + tinyjpeg_get_errorstring(data->jdec)); + errno = EAGAIN; + return -1; + } else { + /* If the JPEG header checked out ok and we get an error during actual + decompression, log the error, but don't return an errorcode to the + application, so that the user gets what we managed to decompress */ + fprintf(stderr, "libv4lconvert: Error decompressing JPEG: %s", + tinyjpeg_get_errorstring(data->jdec)); + } + } break; case V4L2_PIX_FMT_SBGGR8: case V4L2_PIX_FMT_SGBRG8: case V4L2_PIX_FMT_SGRBG8: case V4L2_PIX_FMT_SRGGB8: - if (dest_fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_BGR24) + switch (dest_fmt->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_RGB24: + v4lconvert_bayer_to_rgb24(src, dest, dest_fmt->fmt.pix.width, + dest_fmt->fmt.pix.height, src_fmt->fmt.pix.pixelformat); + break; + case V4L2_PIX_FMT_BGR24: v4lconvert_bayer_to_bgr24(src, dest, dest_fmt->fmt.pix.width, dest_fmt->fmt.pix.height, src_fmt->fmt.pix.pixelformat); - else + break; + default: v4lconvert_bayer_to_yuv420(src, dest, dest_fmt->fmt.pix.width, dest_fmt->fmt.pix.height, src_fmt->fmt.pix.pixelformat); + break; + } break; + /* YUYV line by line formats */ case V4L2_PIX_FMT_SPCA501: - if (dest_fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_BGR24) - v4lconvert_spca501_to_bgr24(src, dest, dest_fmt->fmt.pix.width, - dest_fmt->fmt.pix.height); - else - v4lconvert_spca501_to_yuv420(src, dest, dest_fmt->fmt.pix.width, - dest_fmt->fmt.pix.height); + case V4L2_PIX_FMT_SPCA505: + case V4L2_PIX_FMT_SPCA508: + { + unsigned char tmpbuf[dest_fmt->fmt.pix.width * dest_fmt->fmt.pix.height * + 3 / 2]; + unsigned char *my_dst = (dest_fmt->fmt.pix.pixelformat != + V4L2_PIX_FMT_YUV420) ? tmpbuf : dest; + + switch (src_fmt->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_SPCA501: + v4lconvert_spca501_to_yuv420(src, my_dst, dest_fmt->fmt.pix.width, + dest_fmt->fmt.pix.height); + break; + case V4L2_PIX_FMT_SPCA505: + v4lconvert_spca505_to_yuv420(src, my_dst, dest_fmt->fmt.pix.width, + dest_fmt->fmt.pix.height); + break; + case V4L2_PIX_FMT_SPCA508: + v4lconvert_spca508_to_yuv420(src, my_dst, dest_fmt->fmt.pix.width, + dest_fmt->fmt.pix.height); + break; + } + + switch (dest_fmt->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_RGB24: + v4lconvert_yuv420_to_rgb24(tmpbuf, dest, dest_fmt->fmt.pix.width, + dest_fmt->fmt.pix.height); + break; + case V4L2_PIX_FMT_BGR24: + v4lconvert_yuv420_to_bgr24(tmpbuf, dest, dest_fmt->fmt.pix.width, + dest_fmt->fmt.pix.height); + break; + } break; + } /* compressed bayer formats */ case V4L2_PIX_FMT_SPCA561: @@ -336,7 +456,7 @@ int v4lconvert_convert(struct v4lconvert_data *data, case V4L2_PIX_FMT_SN9C10X: v4lconvert_decode_sn9c10x(src, tmpbuf, dest_fmt->fmt.pix.width, dest_fmt->fmt.pix.height); - bayer_fmt = V4L2_PIX_FMT_SGBRG8; + bayer_fmt = V4L2_PIX_FMT_SBGGR8; break; case V4L2_PIX_FMT_PAC207: v4lconvert_decode_pac207(src, tmpbuf, dest_fmt->fmt.pix.width, @@ -345,24 +465,57 @@ int v4lconvert_convert(struct v4lconvert_data *data, break; } - if (dest_fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_BGR24) + switch (dest_fmt->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_RGB24: + v4lconvert_bayer_to_rgb24(tmpbuf, dest, dest_fmt->fmt.pix.width, + dest_fmt->fmt.pix.height, bayer_fmt); + break; + case V4L2_PIX_FMT_BGR24: v4lconvert_bayer_to_bgr24(tmpbuf, dest, dest_fmt->fmt.pix.width, dest_fmt->fmt.pix.height, bayer_fmt); - else + break; + default: v4lconvert_bayer_to_yuv420(tmpbuf, dest, dest_fmt->fmt.pix.width, dest_fmt->fmt.pix.height, bayer_fmt); + break; + } break; } + case V4L2_PIX_FMT_RGB24: + switch (dest_fmt->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_BGR24: + v4lconvert_swap_rgb(src, dest, dest_fmt->fmt.pix.width, + dest_fmt->fmt.pix.height); + break; + case V4L2_PIX_FMT_YUV420: + printf("FIXME add rgb24 -> yuv420 conversion\n"); + break; + } + break; case V4L2_PIX_FMT_BGR24: - /* dest must be V4L2_PIX_FMT_YUV420 then */ - printf("FIXME add bgr24 -> yuv420 conversion\n"); + switch (dest_fmt->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_RGB24: + v4lconvert_swap_rgb(src, dest, dest_fmt->fmt.pix.width, + dest_fmt->fmt.pix.height); + break; + case V4L2_PIX_FMT_YUV420: + printf("FIXME add bgr24 -> yuv420 conversion\n"); + break; + } break; case V4L2_PIX_FMT_YUV420: - /* dest must be V4L2_PIX_FMT_BGR24 then */ - v4lconvert_yuv420_to_bgr24(src, dest, dest_fmt->fmt.pix.width, - dest_fmt->fmt.pix.height); + switch (dest_fmt->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_RGB24: + v4lconvert_yuv420_to_rgb24(src, dest, dest_fmt->fmt.pix.width, + dest_fmt->fmt.pix.height); + break; + case V4L2_PIX_FMT_BGR24: + v4lconvert_yuv420_to_bgr24(src, dest, dest_fmt->fmt.pix.width, + dest_fmt->fmt.pix.height); + break; + } break; default: @@ -371,6 +524,45 @@ int v4lconvert_convert(struct v4lconvert_data *data, return -1; } + /* Note when rotating dest is our temporary buffer to which our conversion + was done and _dest is the real dest! If the formats are identical no + conversion has been done! */ + if (rotate && dest_fmt->fmt.pix.pixelformat == src_fmt->fmt.pix.pixelformat) + dest = src; + + switch (rotate) { + case 0: + break; + case 90: + switch (dest_fmt->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_RGB24: + case V4L2_PIX_FMT_BGR24: + v4lconvert_rotate90_rgbbgr24(dest, _dest, dest_fmt->fmt.pix.width, + dest_fmt->fmt.pix.height); + break; + case V4L2_PIX_FMT_YUV420: + v4lconvert_rotate90_yuv420(dest, _dest, dest_fmt->fmt.pix.width, + dest_fmt->fmt.pix.height); + break; + } + break; + case 180: + switch (dest_fmt->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_RGB24: + case V4L2_PIX_FMT_BGR24: + v4lconvert_rotate180_rgbbgr24(dest, _dest, dest_fmt->fmt.pix.width, + dest_fmt->fmt.pix.height); + break; + case V4L2_PIX_FMT_YUV420: + v4lconvert_rotate180_yuv420(dest, _dest, dest_fmt->fmt.pix.width, + dest_fmt->fmt.pix.height); + break; + } + break; + default: + printf("FIXME add %d degrees rotation\n", rotate); + } + return needed; } diff --git a/v4l2-apps/lib/libv4l/libv4lconvert/pac207.c b/v4l2-apps/lib/libv4l/libv4lconvert/pac207.c index 085d7a772..97ef4ebc0 100644 --- a/v4l2-apps/lib/libv4l/libv4lconvert/pac207.c +++ b/v4l2-apps/lib/libv4l/libv4lconvert/pac207.c @@ -12,7 +12,7 @@ # 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. +# 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 @@ -38,7 +38,7 @@ static struct { signed char val; } table[256]; -void init_pixart_decoder(void) +static void init_pixart_decoder(void) { int i; int is_abs, val, len; diff --git a/v4l2-apps/lib/libv4l/libv4lconvert/rgbyuv.c b/v4l2-apps/lib/libv4l/libv4lconvert/rgbyuv.c index 79c8ecb35..883d62330 100644 --- a/v4l2-apps/lib/libv4l/libv4lconvert/rgbyuv.c +++ b/v4l2-apps/lib/libv4l/libv4lconvert/rgbyuv.c @@ -12,7 +12,7 @@ # 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. +# 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 @@ -20,6 +20,8 @@ */ +#include "libv4lconvert-priv.h" + #define RGB2YUV(r,g,b,y,u,v) \ (y) = (( 8453*(r) + 16594*(g) + 3223*(b) + 524288) >> 15); \ (u) = (( -4878*(r) - 9578*(g) + 14456*(b) + 4210688) >> 15); \ @@ -80,3 +82,65 @@ void v4lconvert_yuv420_to_bgr24(const unsigned char *src, unsigned char *dest, } } } + +void v4lconvert_yuv420_to_rgb24(const unsigned char *src, unsigned char *dest, + int width, int height) +{ + int i,j; + + const unsigned char *ysrc = src; + const unsigned char *usrc = src + width * height; + const unsigned char *vsrc = usrc + (width * height) / 4; + + for (i = 0; i < height; i++) { + for (j = 0; j < width; j += 2) { +#if 1 /* fast slightly less accurate multiplication free code */ + int u1 = (((*usrc - 128) << 7) + (*usrc - 128)) >> 6; + int rg = (((*usrc - 128) << 1) + (*usrc - 128) + + ((*vsrc - 128) << 2) + ((*vsrc - 128) << 1)) >> 3; + int v1 = (((*vsrc - 128) << 1) + (*vsrc - 128)) >> 1; + + *dest++ = CLIP(*ysrc + v1); + *dest++ = CLIP(*ysrc - rg); + *dest++ = CLIP(*ysrc + u1); + ysrc++; + + *dest++ = CLIP(*ysrc + v1); + *dest++ = CLIP(*ysrc - rg); + *dest++ = CLIP(*ysrc + u1); +#else + *dest++ = YUV2R(*ysrc, *usrc, *vsrc); + *dest++ = YUV2G(*ysrc, *usrc, *vsrc); + *dest++ = YUV2B(*ysrc, *usrc, *vsrc); + ysrc++; + + *dest++ = YUV2R(*ysrc, *usrc, *vsrc); + *dest++ = YUV2G(*ysrc, *usrc, *vsrc); + *dest++ = YUV2B(*ysrc, *usrc, *vsrc); +#endif + ysrc++; + usrc++; + vsrc++; + } + /* Rewind u and v for next line */ + if (i&1) { + usrc -= width / 2; + vsrc -= width / 2; + } + } +} + +void v4lconvert_swap_rgb(const unsigned char *src, unsigned char *dst, + int width, int height) +{ + int i; + + for (i = 0; i < (width * height); i++) { + unsigned char tmp0, tmp1; + tmp0 = *src++; + tmp1 = *src++; + *dst++ = *src++; + *dst++ = tmp1; + *dst++ = tmp0; + } +} diff --git a/v4l2-apps/lib/libv4l/libv4lconvert/sn9c10x.c b/v4l2-apps/lib/libv4l/libv4lconvert/sn9c10x.c index b23ad4630..4ea526d49 100644 --- a/v4l2-apps/lib/libv4l/libv4lconvert/sn9c10x.c +++ b/v4l2-apps/lib/libv4l/libv4lconvert/sn9c10x.c @@ -10,7 +10,7 @@ # 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. +# 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 @@ -50,7 +50,7 @@ static int sonix_unknown = 0; present at the MSB of byte x. */ -void sonix_decompress_init(void) +static void sonix_decompress_init(void) { int i; int is_abs, val, len, unk; diff --git a/v4l2-apps/lib/libv4l/libv4lconvert/spca501.c b/v4l2-apps/lib/libv4l/libv4lconvert/spca501.c index b0170e7cb..9157629e3 100644 --- a/v4l2-apps/lib/libv4l/libv4lconvert/spca501.c +++ b/v4l2-apps/lib/libv4l/libv4lconvert/spca501.c @@ -9,7 +9,7 @@ # 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. +# 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 @@ -18,14 +18,14 @@ #include "libv4lconvert-priv.h" +/* YUYV per line */ void v4lconvert_spca501_to_yuv420(const unsigned char *src, unsigned char *dst, int width, int height) { int i,j; + unsigned long *lsrc = (unsigned long *)src; for (i = 0; i < height; i += 2) { - unsigned long *lsrc = (unsigned long *)(src + (i / 2) * 3 * width); - /* -128 - 127 --> 0 - 255 and copy first line Y */ unsigned long *ldst = (unsigned long *)(dst + i * width); for (j = 0; j < width; j += sizeof(long)) { @@ -56,12 +56,71 @@ void v4lconvert_spca501_to_yuv420(const unsigned char *src, unsigned char *dst, } } -/* IMPROVEME (maybe?) make this convert in one go?? */ -void v4lconvert_spca501_to_bgr24(const unsigned char *src, unsigned char *dst, +/* YYUV per line */ +void v4lconvert_spca505_to_yuv420(const unsigned char *src, unsigned char *dst, int width, int height) { - unsigned char buf[(width * height * 6) / 4]; + int i,j; + unsigned long *lsrc = (unsigned long *)src; + + for (i = 0; i < height; i += 2) { + /* -128 - 127 --> 0 - 255 and copy 2 lines of Y */ + unsigned long *ldst = (unsigned long *)(dst + i * width); + for (j = 0; j < width*2; j += sizeof(long)) { + *ldst = *lsrc++; + *ldst++ ^= 0x8080808080808080ULL; + } + + /* -128 - 127 --> 0 - 255 and copy 1 line U */ + ldst = (unsigned long *)(dst + width * height + i * width / 4); + for (j = 0; j < width/2; j += sizeof(long)) { + *ldst = *lsrc++; + *ldst++ ^= 0x8080808080808080ULL; + } - v4lconvert_spca501_to_yuv420(src, buf, width, height); - v4lconvert_yuv420_to_bgr24(buf, dst, width, height); + /* -128 - 127 --> 0 - 255 and copy 1 line V */ + ldst = (unsigned long *)(dst + (width * height * 5) / 4 + i * width / 4); + for (j = 0; j < width/2; j += sizeof(long)) { + *ldst = *lsrc++; + *ldst++ ^= 0x8080808080808080ULL; + } + } +} + +/* YUVY per line */ +void v4lconvert_spca508_to_yuv420(const unsigned char *src, unsigned char *dst, + int width, int height) +{ + int i,j; + unsigned long *lsrc = (unsigned long *)src; + + for (i = 0; i < height; i += 2) { + /* -128 - 127 --> 0 - 255 and copy first line Y */ + unsigned long *ldst = (unsigned long *)(dst + i * width); + for (j = 0; j < width; j += sizeof(long)) { + *ldst = *lsrc++; + *ldst++ ^= 0x8080808080808080ULL; + } + + /* -128 - 127 --> 0 - 255 and copy 1 line U */ + ldst = (unsigned long *)(dst + width * height + i * width / 4); + for (j = 0; j < width/2; j += sizeof(long)) { + *ldst = *lsrc++; + *ldst++ ^= 0x8080808080808080ULL; + } + + /* -128 - 127 --> 0 - 255 and copy 1 line V */ + ldst = (unsigned long *)(dst + (width * height * 5) / 4 + i * width / 4); + for (j = 0; j < width/2; j += sizeof(long)) { + *ldst = *lsrc++; + *ldst++ ^= 0x8080808080808080ULL; + } + + /* -128 - 127 --> 0 - 255 and copy second line Y */ + ldst = (unsigned long *)(dst + i * width + width); + for (j = 0; j < width; j += sizeof(long)) { + *ldst = *lsrc++; + *ldst++ ^= 0x8080808080808080ULL; + } + } } diff --git a/v4l2-apps/lib/libv4l/libv4lconvert/spca561-decompress.c b/v4l2-apps/lib/libv4l/libv4lconvert/spca561-decompress.c index 40b0f90db..01eed4ec5 100644 --- a/v4l2-apps/lib/libv4l/libv4lconvert/spca561-decompress.c +++ b/v4l2-apps/lib/libv4l/libv4lconvert/spca561-decompress.c @@ -10,7 +10,7 @@ # 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. +# 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 @@ -28,6 +28,7 @@ * but it might work with other spca561 cameras */ #include <string.h> +#include "libv4lconvert-priv.h" /*fixme: not reentrant */ static unsigned int bit_bucket; diff --git a/v4l2-apps/lib/libv4l/libv4lconvert/tinyjpeg.c b/v4l2-apps/lib/libv4l/libv4lconvert/tinyjpeg.c index 5c3b4e26d..fc9efbeb2 100644 --- a/v4l2-apps/lib/libv4l/libv4lconvert/tinyjpeg.c +++ b/v4l2-apps/lib/libv4l/libv4lconvert/tinyjpeg.c @@ -214,6 +214,28 @@ static const unsigned char val_ac_chrominance[] = 0xf9, 0xfa }; +const unsigned char pixart_quantization[][64] = { + { + 0x07, 0x07, 0x08, 0x0a, 0x09, 0x07, 0x0d, 0x0b, + 0x0c, 0x0d, 0x11, 0x10, 0x0f, 0x12, 0x17, 0x27, + 0x1a, 0x18, 0x16, 0x16, 0x18, 0x31, 0x23, 0x25, + 0x1d, 0x28, 0x3a, 0x33, 0x3d, 0x3c, 0x39, 0x33, + 0x38, 0x37, 0x40, 0x48, 0x5c, 0x4e, 0x40, 0x44, + 0x57, 0x45, 0x37, 0x38, 0x50, 0x6d, 0x51, 0x57, + 0x5f, 0x62, 0x67, 0x68, 0x67, 0x3e, 0x4d, 0x71, + 0x79, 0x70, 0x64, 0x78, 0x5c, 0x65, 0x67, 0x63, + }, + { + 0x11, 0x12, 0x12, 0x18, 0x15, 0x18, 0x2f, 0x1a, + 0x1a, 0x2f, 0x63, 0x42, 0x38, 0x42, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + }, +}; /* * 4 functions to manage the stream @@ -246,7 +268,7 @@ static const unsigned char val_ac_chrominance[] = unsigned char c; \ if (stream >= priv->stream_end) { \ snprintf(priv->error_string, sizeof(priv->error_string), \ - "fill_nbits error: need %d more bits\n", \ + "fill_nbits error: need %u more bits\n", \ nbits_wanted - nbits_in_reservoir); \ longjmp(priv->jump_state, -EIO); \ } \ @@ -288,6 +310,74 @@ static const unsigned char val_ac_chrominance[] = } while(0); +/* Special Pixart versions of the *_nbits functions, these remove the special + ff ff ff xx sequences pixart cams insert from the bitstream */ +#define pixart_fill_nbits(reservoir,nbits_in_reservoir,stream,nbits_wanted) \ +do { \ + while (nbits_in_reservoir<nbits_wanted) \ + { \ + unsigned char c; \ + if (stream >= priv->stream_end) { \ + snprintf(priv->error_string, sizeof(priv->error_string), \ + "fill_nbits error: need %u more bits\n", \ + nbits_wanted - nbits_in_reservoir); \ + longjmp(priv->jump_state, -EIO); \ + } \ + c = *stream++; \ + reservoir <<= 8; \ + if (c == 0xff) { \ + switch (stream[0]) { \ + case 0x00: \ + stream++; \ + break; \ + case 0xd9: /* EOF marker */ \ + stream++; \ + if (stream != priv->stream_end) { \ + snprintf(priv->error_string, sizeof(priv->error_string), \ + "Pixart JPEG error: premature EOF\n"); \ + longjmp(priv->jump_state, -EIO); \ + } \ + break; \ + case 0xff: \ + if (stream[1] == 0xff && (stream[2] < 7 || stream[2] == 0xff)) { \ + stream += 3; \ + c = *stream++; \ + break; \ + } \ + /* Error fall through */ \ + default: \ + snprintf(priv->error_string, sizeof(priv->error_string), \ + "Pixart JPEG error: invalid JPEG marker: 0xff 0x%02x 0x%02x 0x%02x\n", \ + (unsigned int)stream[0], (unsigned int)stream[1], \ + (unsigned int)stream[2]); \ + longjmp(priv->jump_state, -EIO); \ + } \ + } \ + reservoir |= c; \ + nbits_in_reservoir+=8; \ + } \ +} while(0); + +/* Signed version !!!! */ +#define pixart_get_nbits(reservoir,nbits_in_reservoir,stream,nbits_wanted,result) \ +do { \ + pixart_fill_nbits(reservoir,nbits_in_reservoir,stream,(nbits_wanted)); \ + result = ((reservoir)>>(nbits_in_reservoir-(nbits_wanted))); \ + nbits_in_reservoir -= (nbits_wanted); \ + reservoir &= ((1U<<nbits_in_reservoir)-1); \ + if ((unsigned int)result < (1UL<<((nbits_wanted)-1))) \ + result += (0xFFFFFFFFUL<<(nbits_wanted))+1; \ +} while(0); + +#define pixart_look_nbits(reservoir,nbits_in_reservoir,stream,nbits_wanted,result) \ +do { \ + pixart_fill_nbits(reservoir,nbits_in_reservoir,stream,(nbits_wanted)); \ + result = ((reservoir)>>(nbits_in_reservoir-(nbits_wanted))); \ +} while(0); + +/* Note skip_nbits is identical for both */ + + #define be16_to_cpu(x) (((x)[0]<<8)|(x)[1]) static void resync(struct jdec_private *priv); @@ -334,9 +424,50 @@ static int get_next_huffman_code(struct jdec_private *priv, struct huffman_table slowtable+=2; } } + snprintf(priv->error_string, sizeof(priv->error_string), + "unknown huffman code: %08x\n", (unsigned int)hcode); + longjmp(priv->jump_state, -EIO); return 0; } +/* identical as above but with *_nbits replaced with pixart_*_nbits */ +static int pixart_get_next_huffman_code(struct jdec_private *priv, + struct huffman_table *huffman_table) +{ + int value, hcode; + unsigned int extra_nbits, nbits; + uint16_t *slowtable; + + pixart_look_nbits(priv->reservoir, priv->nbits_in_reservoir, priv->stream, HUFFMAN_HASH_NBITS, hcode); + value = huffman_table->lookup[hcode]; + if (value >= 0) + { + unsigned int code_size = huffman_table->code_size[value]; + skip_nbits(priv->reservoir, priv->nbits_in_reservoir, priv->stream, code_size); + return value; + } + + /* Decode more bits each time ... */ + for (extra_nbits=0; extra_nbits<16-HUFFMAN_HASH_NBITS; extra_nbits++) + { + nbits = HUFFMAN_HASH_NBITS + 1 + extra_nbits; + + pixart_look_nbits(priv->reservoir, priv->nbits_in_reservoir, priv->stream, nbits, hcode); + slowtable = huffman_table->slowtable[extra_nbits]; + /* Search if the code is in this array */ + while (slowtable[0]) { + if (slowtable[0] == hcode) { + skip_nbits(priv->reservoir, priv->nbits_in_reservoir, priv->stream, nbits); + return slowtable[1]; + } + slowtable+=2; + } + } + snprintf(priv->error_string, sizeof(priv->error_string), + "unknown huffman code: %08x\n", (unsigned int)hcode); + longjmp(priv->jump_state, -EIO); + return 0; +} @@ -388,15 +519,84 @@ static void process_Huffman_data_unit(struct jdec_private *priv, int component) else { j += count_0; /* skip count_0 zeroes */ - get_nbits(priv->reservoir, priv->nbits_in_reservoir, priv->stream, size_val, DCT[j]); - j++; + if (j < 64) { + get_nbits(priv->reservoir, priv->nbits_in_reservoir, priv->stream, size_val, DCT[j]); + j++; + } } } + if (j > 64) { + snprintf(priv->error_string, sizeof(priv->error_string), + "error: more then 63 AC components (%d) in huffman unit\n", (int)j); + longjmp(priv->jump_state, -EIO); + } + for (j = 0; j < 64; j++) c->DCT[j] = DCT[zigzag[j]]; } +/* identical as above both with *_nbits replaced with pixart_*_nbits */ +static void pixart_process_Huffman_data_unit(struct jdec_private *priv, int component) +{ + unsigned char j; + unsigned int huff_code; + unsigned char size_val, count_0; + + struct component *c = &priv->component_infos[component]; + short int DCT[64]; + + /* Initialize the DCT coef table */ + memset(DCT, 0, sizeof(DCT)); + + /* DC coefficient decoding */ + huff_code = pixart_get_next_huffman_code(priv, c->DC_table); + if (huff_code) { + pixart_get_nbits(priv->reservoir, priv->nbits_in_reservoir, priv->stream, huff_code, DCT[0]); + DCT[0] += c->previous_DC; + c->previous_DC = DCT[0]; + } else { + DCT[0] = c->previous_DC; + } + + + /* AC coefficient decoding */ + j = 1; + while (j<64) + { + huff_code = pixart_get_next_huffman_code(priv, c->AC_table); + + size_val = huff_code & 0xF; + count_0 = huff_code >> 4; + + if (size_val == 0) + { /* RLE */ + if (count_0 == 0) + break; /* EOB found, go out */ + else if (count_0 == 0xF) + j += 16; /* skip 16 zeros */ + } + else + { + j += count_0; /* skip count_0 zeroes */ + if (j < 64 ) { + pixart_get_nbits(priv->reservoir, priv->nbits_in_reservoir, priv->stream, size_val, DCT[j]); + j++; + } + } + } + + if (j > 64) { + snprintf(priv->error_string, sizeof(priv->error_string), + "error: more then 63 AC components (%d) in huffman unit\n", (int)j); + longjmp(priv->jump_state, -EIO); + } + + for (j = 0; j < 64; j++) + c->DCT[j] = DCT[zigzag[j]]; +} + + /* * Takes two array of bits, and build the huffman table for size, and code * @@ -404,12 +604,13 @@ static void process_Huffman_data_unit(struct jdec_private *priv, int component) * code_size will be used to known how many bits this symbol is encoded. * slowtable will be used when the first lookup didn't give the result. */ -static void build_huffman_table(const unsigned char *bits, const unsigned char *vals, struct huffman_table *table) +static int build_huffman_table(struct jdec_private *priv, const unsigned char *bits, const unsigned char *vals, struct huffman_table *table) { unsigned int i, j, code, code_size, val, nbits; unsigned char huffsize[257], *hz; unsigned int huffcode[257], *hc; int next_free_entry; + int slowtable_used[16-HUFFMAN_HASH_NBITS]; /* * Build a temp array @@ -425,7 +626,7 @@ static void build_huffman_table(const unsigned char *bits, const unsigned char * memset(table->lookup, 0xff, sizeof(table->lookup)); for (i=0; i<(16-HUFFMAN_HASH_NBITS); i++) - table->slowtable[i][0] = 0; + slowtable_used[i] = 0; /* Build a temp array * huffcode[X] => code used to write vals[X] @@ -472,32 +673,43 @@ static void build_huffman_table(const unsigned char *bits, const unsigned char * else { /* Perhaps sorting the array will be an optimization */ - uint16_t *slowtable = table->slowtable[code_size-HUFFMAN_HASH_NBITS-1]; - while(slowtable[0]) - slowtable+=2; - slowtable[0] = code; - slowtable[1] = val; - slowtable[2] = 0; - /* TODO: NEED TO CHECK FOR AN OVERFLOW OF THE TABLE */ - } + int slowtable_index = code_size-HUFFMAN_HASH_NBITS-1; + + if (slowtable_used[slowtable_index] == 254) + error("slow Huffman table overflow\n"); + table->slowtable[slowtable_index][slowtable_used[slowtable_index]] + = code; + table->slowtable[slowtable_index][slowtable_used[slowtable_index] + 1] + = val; + slowtable_used[slowtable_index] += 2; + } } + for (i=0; i<(16-HUFFMAN_HASH_NBITS); i++) + table->slowtable[i][slowtable_used[i]] = 0; + + return 0; } -static void build_default_huffman_tables(struct jdec_private *priv) +static int build_default_huffman_tables(struct jdec_private *priv) { if ( (priv->flags & TINYJPEG_FLAGS_MJPEG_TABLE) && priv->default_huffman_table_initialized) - return; + return 0; - build_huffman_table(bits_dc_luminance, val_dc_luminance, &priv->HTDC[0]); - build_huffman_table(bits_ac_luminance, val_ac_luminance, &priv->HTAC[0]); + if (build_huffman_table(priv, bits_dc_luminance, val_dc_luminance, &priv->HTDC[0])) + return -1; + if (build_huffman_table(priv, bits_ac_luminance, val_ac_luminance, &priv->HTAC[0])) + return -1; - build_huffman_table(bits_dc_chrominance, val_dc_chrominance, &priv->HTDC[1]); - build_huffman_table(bits_ac_chrominance, val_ac_chrominance, &priv->HTAC[1]); + if (build_huffman_table(priv, bits_dc_chrominance, val_dc_chrominance, &priv->HTDC[1])) + return -1; + if (build_huffman_table(priv, bits_ac_chrominance, val_ac_chrominance, &priv->HTAC[1])) + return -1; priv->default_huffman_table_initialized = 1; + return 0; } @@ -1390,6 +1602,44 @@ static void decode_MCU_2x1_3planes(struct jdec_private *priv) IDCT(&priv->component_infos[cCr], priv->Cr, 8); } +static void pixart_decode_MCU_2x1_3planes(struct jdec_private *priv) +{ + unsigned char marker; + + pixart_look_nbits(priv->reservoir, priv->nbits_in_reservoir, priv->stream, + 8, marker); + /* I think the marker indicates which quantization table to use, iow + a Pixart JPEG may have a different quantization table per MCU, most + MCU's have 0x44 as marker for which our special Pixart quantization + tables are correct. Unfortunately with a 7302 some blocks also have 0x48, + and sometimes even other values. As 0x48 is quite common too, we really + need to find out the correct table for that, as currently the blocks + with a 0x48 marker look wrong. During normal operation the marker stays + within the range below, if it gets out of this range we're most likely + decoding garbage */ + if (marker < 0x20 || marker > 0x7f) { + snprintf(priv->error_string, sizeof(priv->error_string), + "Pixart JPEG error: invalid MCU marker: 0x%02x\n", + (unsigned int)marker); + longjmp(priv->jump_state, -EIO); + } + skip_nbits(priv->reservoir, priv->nbits_in_reservoir, priv->stream, 8); + + // Y + pixart_process_Huffman_data_unit(priv, cY); + IDCT(&priv->component_infos[cY], priv->Y, 16); + pixart_process_Huffman_data_unit(priv, cY); + IDCT(&priv->component_infos[cY], priv->Y+8, 16); + + // Cb + pixart_process_Huffman_data_unit(priv, cCb); + IDCT(&priv->component_infos[cCb], priv->Cb, 8); + + // Cr + pixart_process_Huffman_data_unit(priv, cCr); + IDCT(&priv->component_infos[cCr], priv->Cr, 8); +} + /* * Decode a 2x1 * .-------. @@ -1676,9 +1926,9 @@ static int parse_SOS(struct jdec_private *priv, const unsigned char *stream) error("We do not support more than %d DC Huffman table\n", HUFFMAN_TABLES); if (cid != priv->component_infos[i].cid) - error("SOS cid order (%d:%d) isn't compatible with the SOF marker (%d:%d)\n", + error("SOS cid order (%u:%u) isn't compatible with the SOF marker (%u:%u)\n", i, cid, i, priv->component_infos[i].cid); - trace("ComponentId:%d tableAC:%d tableDC:%d\n", cid, table&0xf, table>>4); + trace("ComponentId:%u tableAC:%d tableDC:%d\n", cid, table&0xf, table>>4); #endif priv->component_infos[i].AC_table = &priv->HTAC[table&0xf]; priv->component_infos[i].DC_table = &priv->HTDC[table>>4]; @@ -1723,10 +1973,13 @@ static int parse_DHT(struct jdec_private *priv, const unsigned char *stream) trace("Length of the table: %d\n", count); #endif - if (index & 0xf0 ) - build_huffman_table(huff_bits, stream, &priv->HTAC[index&0xf]); - else - build_huffman_table(huff_bits, stream, &priv->HTDC[index&0xf]); + if (index & 0xf0 ) { + if (build_huffman_table(priv, huff_bits, stream, &priv->HTAC[index&0xf])) + return -1; + } else { + if (build_huffman_table(priv, huff_bits, stream, &priv->HTDC[index&0xf])) + return -1; + } length -= 1; length -= 16; @@ -1817,6 +2070,8 @@ static int parse_JFIF(struct jdec_private *priv, const unsigned char *stream) { int chuck_len; int marker; + int sof_marker_found = 0; + int dqt_marker_found = 0; int sos_marker_found = 0; int dht_marker_found = 0; const unsigned char *next_chunck; @@ -1838,10 +2093,12 @@ static int parse_JFIF(struct jdec_private *priv, const unsigned char *stream) case SOF: if (parse_SOF(priv, stream) < 0) return -1; + sof_marker_found = 1; break; case DQT: if (parse_DQT(priv, stream) < 0) return -1; + dqt_marker_found = 1; break; case SOS: if (parse_SOS(priv, stream) < 0) @@ -1865,9 +2122,24 @@ static int parse_JFIF(struct jdec_private *priv, const unsigned char *stream) stream = next_chunck; } + if ( !sof_marker_found || + (!dqt_marker_found && !(priv->flags & TINYJPEG_FLAGS_PIXART_JPEG))) + goto bogus_jpeg_format; + + if (priv->flags & TINYJPEG_FLAGS_PIXART_JPEG) { + if (!priv->default_huffman_table_initialized) { + build_quantization_table(priv->Q_tables[0], pixart_quantization[0]); + build_quantization_table(priv->Q_tables[1], pixart_quantization[1]); + } + + /* Pixart JPEG data starts with one unknown / unused byte */ + priv->stream++; + } + if (!dht_marker_found) { trace("No Huffman table loaded, using the default one\n"); - build_default_huffman_tables(priv); + if (build_default_huffman_tables(priv)) + return -1; } #ifdef SANITY_CHECK @@ -1962,6 +2234,13 @@ static const decode_MCU_fct decode_mcu_3comp_table[4] = { decode_MCU_2x2_3planes, }; +static const decode_MCU_fct pixart_decode_mcu_3comp_table[4] = { + NULL, + NULL, + pixart_decode_MCU_2x1_3planes, + NULL, +}; + static const decode_MCU_fct decode_mcu_1comp_table[4] = { decode_MCU_1x1_1plane, decode_MCU_1x2_1plane, @@ -2021,6 +2300,9 @@ int tinyjpeg_decode(struct jdec_private *priv, int pixfmt) bytes_per_blocklines[2] = 0; decode_mcu_table = decode_mcu_3comp_table; + if (priv->flags & TINYJPEG_FLAGS_PIXART_JPEG) + decode_mcu_table = pixart_decode_mcu_3comp_table; + switch (pixfmt) { case TINYJPEG_FMT_YUV420P: colorspace_array_conv = convert_colorspace_yuv420p; @@ -2056,6 +2338,8 @@ int tinyjpeg_decode(struct jdec_private *priv, int pixfmt) case TINYJPEG_FMT_GREY: decode_mcu_table = decode_mcu_1comp_table; + if (priv->flags & TINYJPEG_FLAGS_PIXART_JPEG) + error("Greyscale output not support for PIXART JPEG's\n"); colorspace_array_conv = convert_colorspace_grey; if (priv->components[0] == NULL) priv->components[0] = (uint8_t *)malloc(priv->width * priv->height); @@ -2064,8 +2348,7 @@ int tinyjpeg_decode(struct jdec_private *priv, int pixfmt) break; default: - trace("Bad pixel format\n"); - return -1; + error("Bad pixel format\n"); } xstride_by_mcu = ystride_by_mcu = 8; @@ -2091,6 +2374,9 @@ int tinyjpeg_decode(struct jdec_private *priv, int pixfmt) trace("Use decode 2x1 sampling\n"); } + if (decode_MCU == NULL) + error("no decode MCU function for this JPEG format (PIXART?)\n"); + resync(priv); /* Don't forget to that block can be either 8 or 16 lines */ @@ -2130,6 +2416,12 @@ int tinyjpeg_decode(struct jdec_private *priv, int pixfmt) } } + if (priv->flags & TINYJPEG_FLAGS_PIXART_JPEG) { + /* Additional sanity check for funky Pixart format */ + if ((priv->stream_end - priv->stream) > 5) + error("Pixart JPEG error, stream does not end with EOF marker\n"); + } + return 0; } diff --git a/v4l2-apps/lib/libv4l/libv4lconvert/tinyjpeg.h b/v4l2-apps/lib/libv4l/libv4lconvert/tinyjpeg.h index 42b60c1ee..b0096f0de 100644 --- a/v4l2-apps/lib/libv4l/libv4lconvert/tinyjpeg.h +++ b/v4l2-apps/lib/libv4l/libv4lconvert/tinyjpeg.h @@ -43,6 +43,7 @@ struct jdec_private; /* Flags that can be set by any applications */ #define TINYJPEG_FLAGS_MJPEG_TABLE (1<<1) +#define TINYJPEG_FLAGS_PIXART_JPEG (1<<2) /* Format accepted in outout */ enum tinyjpeg_fmt { diff --git a/v4l2-apps/test/Makefile b/v4l2-apps/test/Makefile index ae8868c21..faafed14d 100644 --- a/v4l2-apps/test/Makefile +++ b/v4l2-apps/test/Makefile @@ -1,6 +1,6 @@ # Makefile for linuxtv.org v4l2-apps/test -CPPFLAGS += -I../../linux/include +CPPFLAGS += -I../include binaries = ioctl-test \ sliced-vbi-test \ diff --git a/v4l2-apps/test/capture_example.c b/v4l2-apps/test/capture_example.c index bbb0c4e57..a15ef3c09 100644 --- a/v4l2-apps/test/capture_example.c +++ b/v4l2-apps/test/capture_example.c @@ -27,7 +27,7 @@ #include <linux/videodev2.h> -#define CLEAR(x) memset (&(x), 0, sizeof (x)) +#define CLEAR(x) memset(&(x), 0, sizeof(x)) typedef enum { IO_METHOD_READ, @@ -36,60 +36,53 @@ typedef enum { } io_method; struct buffer { - void * start; - size_t length; + void *start; + size_t length; }; -static char * dev_name = NULL; -static io_method io = IO_METHOD_MMAP; -static int fd = -1; -struct buffer * buffers = NULL; -static unsigned int n_buffers = 0; -static int out_buf = 0; +static char *dev_name; +static io_method io = IO_METHOD_MMAP; +static int fd = -1; +struct buffer *buffers; +static unsigned int n_buffers; +static int out_buf; +static int force_format; -static void -errno_exit (const char * s) +static void errno_exit(const char *s) { - fprintf (stderr, "%s error %d, %s\n", - s, errno, strerror (errno)); - - exit (EXIT_FAILURE); + fprintf(stderr, "%s error %d, %s\n", s, errno, strerror(errno)); + exit(EXIT_FAILURE); } -static int -xioctl (int fd, - int request, - void * arg) +static int xioctl(int fh, int request, void *arg) { int r; - do r = ioctl (fd, request, arg); - while (-1 == r && EINTR == errno); + do { + r = ioctl(fh, request, arg); + } while (-1 == r && EINTR == errno); return r; } -static void -process_image (const void * p, - int size) +static void process_image(const void *p, int size) { - if (!out_buf) - fputc ('.', stdout); - else - fwrite (p, size, 1, stdout); + if (out_buf) + fwrite(p, size, 1, stdout); - fflush (stdout); + fflush(stderr); + fprintf(stderr, "."); + fflush(stdout); } -static int -read_frame (void) +static int read_frame(void) { struct v4l2_buffer buf; unsigned int i; switch (io) { case IO_METHOD_READ: - if (-1 == read (fd, buffers[0].start, buffers[0].length)) { + if (-1 == read(fd, buffers[0].start, buffers[0].length)) { switch (errno) { case EAGAIN: return 0; @@ -100,21 +93,20 @@ read_frame (void) /* fall through */ default: - errno_exit ("read"); + errno_exit("read"); } } - process_image (buffers[0].start, buffers[0].length); - + process_image(buffers[0].start, buffers[0].length); break; case IO_METHOD_MMAP: - CLEAR (buf); + CLEAR(buf); buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; - if (-1 == xioctl (fd, VIDIOC_DQBUF, &buf)) { + if (-1 == xioctl(fd, VIDIOC_DQBUF, &buf)) { switch (errno) { case EAGAIN: return 0; @@ -125,26 +117,25 @@ read_frame (void) /* fall through */ default: - errno_exit ("VIDIOC_DQBUF"); + errno_exit("VIDIOC_DQBUF"); } } - assert (buf.index < n_buffers); + assert(buf.index < n_buffers); - process_image (buffers[buf.index].start, buffers[buf.index].length); - - if (-1 == xioctl (fd, VIDIOC_QBUF, &buf)) - errno_exit ("VIDIOC_QBUF"); + process_image(buffers[buf.index].start, buf.bytesused); + if (-1 == xioctl(fd, VIDIOC_QBUF, &buf)) + errno_exit("VIDIOC_QBUF"); break; case IO_METHOD_USERPTR: - CLEAR (buf); + CLEAR(buf); buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_USERPTR; - if (-1 == xioctl (fd, VIDIOC_DQBUF, &buf)) { + if (-1 == xioctl(fd, VIDIOC_DQBUF, &buf)) { switch (errno) { case EAGAIN: return 0; @@ -155,30 +146,28 @@ read_frame (void) /* fall through */ default: - errno_exit ("VIDIOC_DQBUF"); + errno_exit("VIDIOC_DQBUF"); } } for (i = 0; i < n_buffers; ++i) - if (buf.m.userptr == (unsigned long) buffers[i].start + if (buf.m.userptr == (unsigned long)buffers[i].start && buf.length == buffers[i].length) break; - assert (i < n_buffers); - - process_image ((void *) buf.m.userptr, buf.length); + assert(i < n_buffers); - if (-1 == xioctl (fd, VIDIOC_QBUF, &buf)) - errno_exit ("VIDIOC_QBUF"); + process_image((void *)buf.m.userptr, buf.bytesused); + if (-1 == xioctl(fd, VIDIOC_QBUF, &buf)) + errno_exit("VIDIOC_QBUF"); break; } return 1; } -static void -mainloop (void) +static void mainloop(void) { unsigned int count; @@ -190,37 +179,34 @@ mainloop (void) struct timeval tv; int r; - FD_ZERO (&fds); - FD_SET (fd, &fds); + FD_ZERO(&fds); + FD_SET(fd, &fds); /* Timeout. */ tv.tv_sec = 2; tv.tv_usec = 0; - r = select (fd + 1, &fds, NULL, NULL, &tv); + r = select(fd + 1, &fds, NULL, NULL, &tv); if (-1 == r) { if (EINTR == errno) continue; - - errno_exit ("select"); + errno_exit("select"); } if (0 == r) { - fprintf (stderr, "select timeout\n"); - exit (EXIT_FAILURE); + fprintf(stderr, "select timeout\n"); + exit(EXIT_FAILURE); } - if (read_frame ()) + if (read_frame()) break; - /* EAGAIN - continue select loop. */ } } } -static void -stop_capturing (void) +static void stop_capturing(void) { enum v4l2_buf_type type; @@ -232,16 +218,13 @@ stop_capturing (void) case IO_METHOD_MMAP: case IO_METHOD_USERPTR: type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - - if (-1 == xioctl (fd, VIDIOC_STREAMOFF, &type)) - errno_exit ("VIDIOC_STREAMOFF"); - + if (-1 == xioctl(fd, VIDIOC_STREAMOFF, &type)) + errno_exit("VIDIOC_STREAMOFF"); break; } } -static void -start_capturing (void) +static void start_capturing(void) { unsigned int i; enum v4l2_buf_type type; @@ -255,192 +238,179 @@ start_capturing (void) for (i = 0; i < n_buffers; ++i) { struct v4l2_buffer buf; - CLEAR (buf); + CLEAR(buf); + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; + buf.index = i; - buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - buf.memory = V4L2_MEMORY_MMAP; - buf.index = i; - - if (-1 == xioctl (fd, VIDIOC_QBUF, &buf)) - errno_exit ("VIDIOC_QBUF"); + if (-1 == xioctl(fd, VIDIOC_QBUF, &buf)) + errno_exit("VIDIOC_QBUF"); } - type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - - if (-1 == xioctl (fd, VIDIOC_STREAMON, &type)) - errno_exit ("VIDIOC_STREAMON"); - + if (-1 == xioctl(fd, VIDIOC_STREAMON, &type)) + errno_exit("VIDIOC_STREAMON"); break; case IO_METHOD_USERPTR: for (i = 0; i < n_buffers; ++i) { struct v4l2_buffer buf; - CLEAR (buf); + CLEAR(buf); + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_USERPTR; + buf.index = i; + buf.m.userptr = (unsigned long)buffers[i].start; + buf.length = buffers[i].length; - buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - buf.memory = V4L2_MEMORY_USERPTR; - buf.index = i; - buf.m.userptr = (unsigned long) buffers[i].start; - buf.length = buffers[i].length; - - if (-1 == xioctl (fd, VIDIOC_QBUF, &buf)) - errno_exit ("VIDIOC_QBUF"); + if (-1 == xioctl(fd, VIDIOC_QBUF, &buf)) + errno_exit("VIDIOC_QBUF"); } - type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - - if (-1 == xioctl (fd, VIDIOC_STREAMON, &type)) - errno_exit ("VIDIOC_STREAMON"); - + if (-1 == xioctl(fd, VIDIOC_STREAMON, &type)) + errno_exit("VIDIOC_STREAMON"); break; } } -static void -uninit_device (void) +static void uninit_device(void) { unsigned int i; switch (io) { case IO_METHOD_READ: - free (buffers[0].start); + free(buffers[0].start); break; case IO_METHOD_MMAP: for (i = 0; i < n_buffers; ++i) - if (-1 == munmap (buffers[i].start, buffers[i].length)) - errno_exit ("munmap"); + if (-1 == munmap(buffers[i].start, buffers[i].length)) + errno_exit("munmap"); break; case IO_METHOD_USERPTR: for (i = 0; i < n_buffers; ++i) - free (buffers[i].start); + free(buffers[i].start); break; } - free (buffers); + free(buffers); } -static void -init_read (unsigned int buffer_size) +static void init_read(unsigned int buffer_size) { - buffers = calloc (1, sizeof (*buffers)); + buffers = calloc(1, sizeof(*buffers)); if (!buffers) { - fprintf (stderr, "Out of memory\n"); - exit (EXIT_FAILURE); + fprintf(stderr, "Out of memory\n"); + exit(EXIT_FAILURE); } buffers[0].length = buffer_size; - buffers[0].start = malloc (buffer_size); + buffers[0].start = malloc(buffer_size); if (!buffers[0].start) { - fprintf (stderr, "Out of memory\n"); - exit (EXIT_FAILURE); + fprintf(stderr, "Out of memory\n"); + exit(EXIT_FAILURE); } } -static void -init_mmap (void) +static void init_mmap(void) { struct v4l2_requestbuffers req; - CLEAR (req); + CLEAR(req); - req.count = 4; - req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - req.memory = V4L2_MEMORY_MMAP; + req.count = 4; + req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + req.memory = V4L2_MEMORY_MMAP; - if (-1 == xioctl (fd, VIDIOC_REQBUFS, &req)) { + if (-1 == xioctl(fd, VIDIOC_REQBUFS, &req)) { if (EINVAL == errno) { - fprintf (stderr, "%s does not support " + fprintf(stderr, "%s does not support " "memory mapping\n", dev_name); - exit (EXIT_FAILURE); + exit(EXIT_FAILURE); } else { - errno_exit ("VIDIOC_REQBUFS"); + errno_exit("VIDIOC_REQBUFS"); } } if (req.count < 2) { - fprintf (stderr, "Insufficient buffer memory on %s\n", + fprintf(stderr, "Insufficient buffer memory on %s\n", dev_name); - exit (EXIT_FAILURE); + exit(EXIT_FAILURE); } - buffers = calloc (req.count, sizeof (*buffers)); + buffers = calloc(req.count, sizeof(*buffers)); if (!buffers) { - fprintf (stderr, "Out of memory\n"); - exit (EXIT_FAILURE); + fprintf(stderr, "Out of memory\n"); + exit(EXIT_FAILURE); } for (n_buffers = 0; n_buffers < req.count; ++n_buffers) { struct v4l2_buffer buf; - CLEAR (buf); + CLEAR(buf); buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; buf.index = n_buffers; - if (-1 == xioctl (fd, VIDIOC_QUERYBUF, &buf)) - errno_exit ("VIDIOC_QUERYBUF"); + if (-1 == xioctl(fd, VIDIOC_QUERYBUF, &buf)) + errno_exit("VIDIOC_QUERYBUF"); buffers[n_buffers].length = buf.length; buffers[n_buffers].start = - mmap (NULL /* start anywhere */, + mmap(NULL /* start anywhere */, buf.length, PROT_READ | PROT_WRITE /* required */, MAP_SHARED /* recommended */, fd, buf.m.offset); if (MAP_FAILED == buffers[n_buffers].start) - errno_exit ("mmap"); + errno_exit("mmap"); } } -static void -init_userp (unsigned int buffer_size) +static void init_userp(unsigned int buffer_size) { struct v4l2_requestbuffers req; - CLEAR (req); + CLEAR(req); - req.count = 4; - req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - req.memory = V4L2_MEMORY_USERPTR; + req.count = 4; + req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + req.memory = V4L2_MEMORY_USERPTR; - if (-1 == xioctl (fd, VIDIOC_REQBUFS, &req)) { + if (-1 == xioctl(fd, VIDIOC_REQBUFS, &req)) { if (EINVAL == errno) { - fprintf (stderr, "%s does not support " + fprintf(stderr, "%s does not support " "user pointer i/o\n", dev_name); - exit (EXIT_FAILURE); + exit(EXIT_FAILURE); } else { - errno_exit ("VIDIOC_REQBUFS"); + errno_exit("VIDIOC_REQBUFS"); } } - buffers = calloc (4, sizeof (*buffers)); + buffers = calloc(4, sizeof(*buffers)); if (!buffers) { - fprintf (stderr, "Out of memory\n"); - exit (EXIT_FAILURE); + fprintf(stderr, "Out of memory\n"); + exit(EXIT_FAILURE); } for (n_buffers = 0; n_buffers < 4; ++n_buffers) { buffers[n_buffers].length = buffer_size; - buffers[n_buffers].start = malloc (buffer_size); + buffers[n_buffers].start = malloc(buffer_size); if (!buffers[n_buffers].start) { - fprintf (stderr, "Out of memory\n"); - exit (EXIT_FAILURE); + fprintf(stderr, "Out of memory\n"); + exit(EXIT_FAILURE); } } } -static void -init_device (void) +static void init_device(void) { struct v4l2_capability cap; struct v4l2_cropcap cropcap; @@ -448,40 +418,38 @@ init_device (void) struct v4l2_format fmt; unsigned int min; - if (-1 == xioctl (fd, VIDIOC_QUERYCAP, &cap)) { + if (-1 == xioctl(fd, VIDIOC_QUERYCAP, &cap)) { if (EINVAL == errno) { - fprintf (stderr, "%s is no V4L2 device\n", + fprintf(stderr, "%s is no V4L2 device\n", dev_name); - exit (EXIT_FAILURE); + exit(EXIT_FAILURE); } else { - errno_exit ("VIDIOC_QUERYCAP"); + errno_exit("VIDIOC_QUERYCAP"); } } if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) { - fprintf (stderr, "%s is no video capture device\n", + fprintf(stderr, "%s is no video capture device\n", dev_name); - exit (EXIT_FAILURE); + exit(EXIT_FAILURE); } switch (io) { case IO_METHOD_READ: if (!(cap.capabilities & V4L2_CAP_READWRITE)) { - fprintf (stderr, "%s does not support read i/o\n", + fprintf(stderr, "%s does not support read i/o\n", dev_name); - exit (EXIT_FAILURE); + exit(EXIT_FAILURE); } - break; case IO_METHOD_MMAP: case IO_METHOD_USERPTR: if (!(cap.capabilities & V4L2_CAP_STREAMING)) { - fprintf (stderr, "%s does not support streaming i/o\n", + fprintf(stderr, "%s does not support streaming i/o\n", dev_name); - exit (EXIT_FAILURE); + exit(EXIT_FAILURE); } - break; } @@ -489,15 +457,15 @@ init_device (void) /* Select video input, video standard and tune here. */ - CLEAR (cropcap); + CLEAR(cropcap); cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - if (0 == xioctl (fd, VIDIOC_CROPCAP, &cropcap)) { + if (0 == xioctl(fd, VIDIOC_CROPCAP, &cropcap)) { crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; crop.c = cropcap.defrect; /* reset to default */ - if (-1 == xioctl (fd, VIDIOC_S_CROP, &crop)) { + if (-1 == xioctl(fd, VIDIOC_S_CROP, &crop)) { switch (errno) { case EINVAL: /* Cropping not supported. */ @@ -512,18 +480,24 @@ init_device (void) } - CLEAR (fmt); + CLEAR(fmt); - fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - fmt.fmt.pix.width = 640; - fmt.fmt.pix.height = 480; - fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; - fmt.fmt.pix.field = V4L2_FIELD_INTERLACED; + fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (force_format) { + fmt.fmt.pix.width = 640; + fmt.fmt.pix.height = 480; + fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; + fmt.fmt.pix.field = V4L2_FIELD_INTERLACED; - if (-1 == xioctl (fd, VIDIOC_S_FMT, &fmt)) - errno_exit ("VIDIOC_S_FMT"); + if (-1 == xioctl(fd, VIDIOC_S_FMT, &fmt)) + errno_exit("VIDIOC_S_FMT"); - /* Note VIDIOC_S_FMT may change width and height. */ + /* Note VIDIOC_S_FMT may change width and height. */ + } else { + /* Preserve original settings as set by v4l2-ctl for example */ + if (-1 == xioctl(fd, VIDIOC_G_FMT, &fmt)) + errno_exit("VIDIOC_G_FMT"); + } /* Buggy driver paranoia. */ min = fmt.fmt.pix.width * 2; @@ -535,97 +509,91 @@ init_device (void) switch (io) { case IO_METHOD_READ: - init_read (fmt.fmt.pix.sizeimage); + init_read(fmt.fmt.pix.sizeimage); break; case IO_METHOD_MMAP: - init_mmap (); + init_mmap(); break; case IO_METHOD_USERPTR: - init_userp (fmt.fmt.pix.sizeimage); + init_userp(fmt.fmt.pix.sizeimage); break; } } -static void -close_device (void) +static void close_device(void) { - if (-1 == close (fd)) - errno_exit ("close"); + if (-1 == close(fd)) + errno_exit("close"); fd = -1; } -static void -open_device (void) +static void open_device(void) { struct stat st; - if (-1 == stat (dev_name, &st)) { - fprintf (stderr, "Cannot identify '%s': %d, %s\n", - dev_name, errno, strerror (errno)); - exit (EXIT_FAILURE); + if (-1 == stat(dev_name, &st)) { + fprintf(stderr, "Cannot identify '%s': %d, %s\n", + dev_name, errno, strerror(errno)); + exit(EXIT_FAILURE); } - if (!S_ISCHR (st.st_mode)) { - fprintf (stderr, "%s is no device\n", dev_name); - exit (EXIT_FAILURE); + if (!S_ISCHR(st.st_mode)) { + fprintf(stderr, "%s is no device\n", dev_name); + exit(EXIT_FAILURE); } - fd = open (dev_name, O_RDWR /* required */ | O_NONBLOCK, 0); + fd = open(dev_name, O_RDWR /* required */ | O_NONBLOCK, 0); if (-1 == fd) { - fprintf (stderr, "Cannot open '%s': %d, %s\n", - dev_name, errno, strerror (errno)); - exit (EXIT_FAILURE); + fprintf(stderr, "Cannot open '%s': %d, %s\n", + dev_name, errno, strerror(errno)); + exit(EXIT_FAILURE); } } -static void -usage (FILE * fp, - int argc, - char ** argv) +static void usage(FILE *fp, int argc, char **argv) { - fprintf (fp, + fprintf(fp, "Usage: %s [options]\n\n" "Options:\n" - "-d | --device name Video device name [/dev/video]\n" + "-d | --device name Video device name [/dev/video0]\n" "-h | --help Print this message\n" "-m | --mmap Use memory mapped buffers\n" "-r | --read Use read() calls\n" "-u | --userp Use application allocated buffers\n" "-o | --output Outputs stream to stdout\n" + "-f | --format Force format to 640x480 YUYV\n" "", argv[0]); } -static const char short_options [] = "d:hmruo"; +static const char short_options[] = "d:hmruof"; static const struct option -long_options [] = { - { "device", required_argument, NULL, 'd' }, - { "help", no_argument, NULL, 'h' }, - { "mmap", no_argument, NULL, 'm' }, - { "read", no_argument, NULL, 'r' }, - { "userp", no_argument, NULL, 'u' }, - { "output", no_argument, NULL, 'o' }, +long_options[] = { + { "device", required_argument, NULL, 'd' }, + { "help", no_argument, NULL, 'h' }, + { "mmap", no_argument, NULL, 'm' }, + { "read", no_argument, NULL, 'r' }, + { "userp", no_argument, NULL, 'u' }, + { "output", no_argument, NULL, 'o' }, + { "format", no_argument, NULL, 'f' }, { 0, 0, 0, 0 } }; -int -main (int argc, - char ** argv) +int main(int argc, char **argv) { - dev_name = "/dev/video"; + dev_name = "/dev/video0"; for (;;) { - int index; + int idx; int c; - c = getopt_long (argc, argv, - short_options, long_options, - &index); + c = getopt_long(argc, argv, + short_options, long_options, &idx); if (-1 == c) break; @@ -639,8 +607,8 @@ main (int argc, break; case 'h': - usage (stdout, argc, argv); - exit (EXIT_SUCCESS); + usage(stdout, argc, argv); + exit(EXIT_SUCCESS); case 'm': io = IO_METHOD_MMAP; @@ -658,27 +626,23 @@ main (int argc, out_buf++; break; + case 'f': + force_format++; + break; + default: - usage (stderr, argc, argv); - exit (EXIT_FAILURE); + usage(stderr, argc, argv); + exit(EXIT_FAILURE); } } - open_device (); - - init_device (); - - start_capturing (); - - mainloop (); - - stop_capturing (); - - uninit_device (); - - close_device (); - - exit (EXIT_SUCCESS); - + open_device(); + init_device(); + start_capturing(); + mainloop(); + stop_capturing(); + uninit_device(); + close_device(); + fprintf(stderr, "\n"); return 0; } diff --git a/v4l2-apps/test/ioctl-test.c b/v4l2-apps/test/ioctl-test.c index f483338fb..99ad717e5 100644 --- a/v4l2-apps/test/ioctl-test.c +++ b/v4l2-apps/test/ioctl-test.c @@ -42,7 +42,7 @@ typedef __u8 u8; typedef __u32 u32; #include <linux/version.h> -#include "../linux/include/media/v4l2-common.h" +#include <media/v4l2-common.h> #include <linux/video_decoder.h> #else typedef u_int32_t u32; diff --git a/v4l2-apps/util/Makefile b/v4l2-apps/util/Makefile index 873553a95..dcef94e0f 100644 --- a/v4l2-apps/util/Makefile +++ b/v4l2-apps/util/Makefile @@ -4,10 +4,10 @@ ifeq ($(KERNEL_DIR),) KERNEL_DIR = /usr endif -CPPFLAGS += -I../../linux/include -D_GNU_SOURCE +CPPFLAGS += -I../include -D_GNU_SOURCE LDFLAGS += -lm -binaries = v4l2-ctl v4l2-dbg ivtv-ctl cx18-ctl v4l-board-dbg +binaries = v4l2-ctl v4l2-dbg v4l2-compliance ivtv-ctl cx18-ctl ifeq ($(prefix),) prefix = /usr @@ -22,14 +22,18 @@ clean:: -if [ -f qv4l2/Makefile ]; then make -C qv4l2 $@; fi -rm -f qv4l2/qv4l2 qv4l2/Makefile make -C xc3028-firmware $@ + rm -f v4l2-driverids.cpp v4l2-chipids.cpp + rm -rf keycodes parse.h keytable qv4l2: - if [ ! -f qv4l2/Makefile ]; then (cd qv4l2; qmake); fi - make -C qv4l2 + -if [ ! -f qv4l2/Makefile ]; then (cd qv4l2; qmake); fi + $(MAKE) -C qv4l2 v4l2-dbg: v4l2-dbg.o v4l2-driverids.o v4l2-chipids.o $(CXX) $^ -o $@ +v4l2-dbg.o: v4l2-dbg.h v4l2-dbg-bttv.h v4l2-dbg-em28xx.h v4l2-dbg-saa7134.h + install: mkdir -p $(prefix)/bin cp $(binaries) $(prefix)/bin @@ -41,7 +45,7 @@ parse.h: $(KERNEL_DIR)/include/linux/input.h @echo -en "struct parse_key {\n\tchar *name;\n\tunsigned int value;\n} " >parse.h @echo -en "keynames[] = {\n" >>parse.h - @more $(KERNEL_DIR)/linux/input.h |perl -n \ + @more $(KERNEL_DIR)/include/linux/input.h |perl -n \ -e 'if (m/^\#define\s+(KEY_[^\s]+)\s+(0x[\d\w]+|[\d]+)/) ' \ -e '{ printf "\t{\"%s\", %s},\n",$$1,$$2; }' \ -e 'if (m/^\#define\s+(BTN_[^\s]+)\s+(0x[\d\w]+|[\d]+)/) ' \ @@ -50,19 +54,17 @@ parse.h: $(KERNEL_DIR)/include/linux/input.h @echo -en "\t{ NULL, 0}\n};\n" >>parse.h keytables: - -mkdir keycodes + -mkdir -p keycodes ./gen_keytables.pl ../../linux/drivers/media/common/ir-keymaps.c keytable: keytable.c parse.h keytables -v4l-board-dbg: v4l-board-dbg.c bttv-dbg.h saa7134-dbg.h em28xx-dbg.h - -v4l2-driverids.cpp: ../../linux/include/linux/i2c-id.h +v4l2-driverids.cpp: ../include/linux/i2c-id.h @echo "struct driverid { const char *name; unsigned id; } driverids[] = {" >$@ @grep I2C_DRIVERID_ $^ | sed -e 's/.*I2C_DRIVERID_\([0-9A-Z_]*\)[^0-9]*\([0-9]*\).*/{ "\1", \2 },/' | tr A-Z a-z >>$@ @echo "{ 0, 0 }};" >>$@ -v4l2-chipids.cpp: ../../linux/include/media/v4l2-chip-ident.h +v4l2-chipids.cpp: ../include/media/v4l2-chip-ident.h @echo "struct chipid { const char *name; unsigned id; } chipids[] = {" >$@ @grep V4L2_IDENT_ $^ | sed -e 's/.*V4L2_IDENT_\([0-9A-Z_]*\)[^=]*=[^0-9]*\([0-9]*\).*/{ "\1", \2 },/' | tr A-Z a-z >>$@ @echo "{ 0, 0 }};" >>$@ diff --git a/v4l2-apps/util/cx18-ctl.c b/v4l2-apps/util/cx18-ctl.c index 755b9437f..4081f3aef 100644 --- a/v4l2-apps/util/cx18-ctl.c +++ b/v4l2-apps/util/cx18-ctl.c @@ -267,7 +267,7 @@ int main(int argc, char **argv) /* bitfield for OptSetCodec */ /* command args */ - char *device = strdup("/dev/video0"); /* -d device */ + const char *device = "/dev/video0"; /* -d device */ int ch; int yuv_mode = 0; struct v4l2_routing route; /* audio_route */ @@ -312,11 +312,13 @@ int main(int argc, char **argv) break; } case OptSetDevice: - device = strdup(optarg); + device = optarg; if (device[0] >= '0' && device[0] <= '9' && device[1] == 0) { + static char newdev[20]; char dev = device[0]; - sprintf(device, "/dev/video%c", dev); + sprintf(newdev, "/dev/video%c", dev); + device = newdev; } break; case OptSetAudioRoute: @@ -410,7 +412,6 @@ int main(int argc, char **argv) strerror(errno)); exit(1); } - free(device); /* Setting Opts */ diff --git a/v4l2-apps/util/ivtv-ctl.c b/v4l2-apps/util/ivtv-ctl.c index 9d3170785..af84164e3 100644 --- a/v4l2-apps/util/ivtv-ctl.c +++ b/v4l2-apps/util/ivtv-ctl.c @@ -343,7 +343,7 @@ int main(int argc, char **argv) /* bitfield for OptSetCodec */ /* command args */ - char *device = strdup("/dev/video0"); /* -d device */ + const char *device = "/dev/video0"; /* -d device */ int ch; int yuv_mode = 0; struct v4l2_routing route; /* audio_route */ @@ -351,9 +351,9 @@ int main(int argc, char **argv) unsigned short gpio_dir = 0x0; /* GPIO direction bits */ int gpio_set_dir = 0; int passthrough = 0; - int audio_mute = 0; - int stereo_mode = 0; - int bilingual_mode = 0; + long audio_mute = 0; + long stereo_mode = 0; + long bilingual_mode = 0; int debug_level = 0; __u32 reset = 0; int new_debug_level, gdebug_level; @@ -415,11 +415,13 @@ int main(int argc, char **argv) break; } case OptSetDevice: - device = strdup(optarg); + device = optarg; if (device[0] >= '0' && device[0] <= '9' && device[1] == 0) { + static char newdev[20]; char dev = device[0]; - sprintf(device, "/dev/video%c", dev); + sprintf(newdev, "/dev/video%c", dev); + device = newdev; } break; case OptSetAudioRoute: @@ -524,7 +526,6 @@ int main(int argc, char **argv) strerror(errno)); exit(1); } - free(device); /* Setting Opts */ @@ -620,7 +621,7 @@ int main(int argc, char **argv) } if (options[OptPassThrough]) { - int source = passthrough ? VIDEO_SOURCE_DEMUX : VIDEO_SOURCE_MEMORY; + long source = passthrough ? VIDEO_SOURCE_DEMUX : VIDEO_SOURCE_MEMORY; doioctl(fd, VIDEO_SELECT_SOURCE, (void *)source, "IVTV_IOC_PASSTHROUGH"); diff --git a/v4l2-apps/util/qv4l2/general-tab.cpp b/v4l2-apps/util/qv4l2/general-tab.cpp index d7f5a98d2..3b9ad2e5b 100644 --- a/v4l2-apps/util/qv4l2/general-tab.cpp +++ b/v4l2-apps/util/qv4l2/general-tab.cpp @@ -42,23 +42,23 @@ GeneralTab::GeneralTab(const char *device, int _fd, int n, QWidget *parent) : memset(&querycap, 0, sizeof(querycap)); if (ioctl(fd, VIDIOC_QUERYCAP, &querycap) >=0) { QLabel *l1 = new QLabel("Device:", this); - QLabel *l1t = new QLabel(device, this); + new QLabel(device, this); l1->setAlignment(Qt::AlignRight); QLabel *l2 = new QLabel("Driver:", this); l2->setAlignment(Qt::AlignRight); - QLabel *l2t = new QLabel((char *)querycap.driver, this); + new QLabel((char *)querycap.driver, this); QLabel *l3 = new QLabel("Card:", this); l3->setAlignment(Qt::AlignRight); - QLabel *l3t = new QLabel((char *)querycap.card, this); + new QLabel((char *)querycap.card, this); QLabel *l4 = new QLabel("Bus:", this); l4->setAlignment(Qt::AlignRight); - QLabel *l4t = new QLabel((char *)querycap.bus_info, this); + new QLabel((char *)querycap.bus_info, this); } memset(&tuner, 0, sizeof(tuner)); diff --git a/v4l2-apps/util/qv4l2/qv4l2.pro b/v4l2-apps/util/qv4l2/qv4l2.pro index c53a098b5..5e0b7555d 100644 --- a/v4l2-apps/util/qv4l2/qv4l2.pro +++ b/v4l2-apps/util/qv4l2/qv4l2.pro @@ -3,7 +3,7 @@ ###################################################################### TEMPLATE = app -INCLUDEPATH += . ../../../linux/include ../../lib +INCLUDEPATH += . ../../include ../../lib CONFIG += debug # Input diff --git a/v4l2-apps/util/rds/Makefile b/v4l2-apps/util/rds/Makefile index 4f6ebd9f2..fb2213c99 100644 --- a/v4l2-apps/util/rds/Makefile +++ b/v4l2-apps/util/rds/Makefile @@ -1,6 +1,6 @@ # Makefile for linuxtv.org v4l2-apps/util/xc3028-firmware -CPPFLAGS += -I../../../linux/include +CPPFLAGS += -I../../include binaries = rds-saa6588 diff --git a/v4l2-apps/util/v4l-board-dbg.c b/v4l2-apps/util/v4l-board-dbg.c deleted file mode 100644 index e74c7300e..000000000 --- a/v4l2-apps/util/v4l-board-dbg.c +++ /dev/null @@ -1,335 +0,0 @@ -/* - Copyright (C) 2008 Mauro Carvalho Chehab <mchehab@infradead.org> - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation version 2 of the License. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include <unistd.h> -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <getopt.h> -#include <fcntl.h> -#include <errno.h> -#include <sys/ioctl.h> -#include <sys/time.h> -#include <linux/types.h> -#include <linux/videodev2.h> - -#include "bttv-dbg.h" -#include "saa7134-dbg.h" -#include "em28xx-dbg.h" - -#define ARRAY_SIZE(arr) ((int)(sizeof(arr) / sizeof((arr)[0]))) - -struct board_list { - char *name; - int prefix; /* Register prefix size */ - struct board_regs *regs; - int regs_size; - struct board_regs *alt_regs; - int alt_regs_size; -}; - -struct board_list boards[] = { - [0] = { /* From bttv-dbg.h */ - .name = BTTV_IDENT, - .prefix = sizeof(BTTV_PREFIX) - 1, - .regs = bt8xx_regs, - .regs_size = ARRAY_SIZE(bt8xx_regs), - .alt_regs = bt8xx_regs_other, - .alt_regs_size = ARRAY_SIZE(bt8xx_regs_other), - }, - [1] = { /* From saa7134-dbg.h */ - .name = SAA7134_IDENT, - .prefix = sizeof(SAA7134_PREFIX) - 1, - .regs = saa7134_regs, - .regs_size = ARRAY_SIZE(saa7134_regs), - .alt_regs = NULL, - .alt_regs_size = 0, - }, - [2] = { /* From em28xx-dbg.h */ - .name = EM28XX_IDENT, - .prefix = sizeof(EM28XX_PREFIX) - 1, - .regs = em28xx_regs, - .regs_size = ARRAY_SIZE(em28xx_regs), - .alt_regs = NULL, - .alt_regs_size = 0, - }, -}; - -static int is_get=0, is_set=0; - -static int doioctl(int fd, int request, void *parm, const char *name) -{ - int retVal; - - printf("ioctl %s ", name); - retVal = ioctl(fd, request, parm); - if (retVal < 0) - printf("failed: %s\n", strerror(errno)); - else - printf("ok\n"); - - return retVal; -} - -static void usage(void) -{ - printf("bttv-dbg <args>\n"); -} - -enum Option { - OptGetReg = 'g', - OptSetReg = 's', - OptHelp = 'h', -}; - -static void print_bin (int val, int size) -{ - int i, j, v; - - printf("("); - for (i = size-1; i >= 0; i--) { - v = (val >> (i * 8)) & 0xff; - for (j = 7; j >= 0; j--) { - int bit = (v >> j) & 0x1; - if (bit) - printf("1"); - else - printf("0"); - } - if (i) - printf(" "); - else - printf(")"); - } -} - -int main(int argc, char **argv) -{ - char *device = strdup("/dev/video0"); - char *reg_set = NULL; - int ch; - int i; - int fd = -1; - struct v4l2_register reg; - struct v4l2_capability cap; - struct board_list *curr_bd; - int board = 0; - struct option long_options[] = { - /* Please keep in alphabetical order of the short option. - That makes it easier to see which options are still free. */ - {"get-reg", no_argument, 0, OptGetReg}, - {"set-reg", required_argument, 0, OptSetReg}, - {"help", no_argument, 0, OptHelp}, - {0, 0, 0, 0} - }; - - /* command args */ - if (argc == 1) { - usage(); - return 0; - } - while (1) { - int option_index = 0; - - ch = getopt_long(argc, argv, "gs:", long_options, &option_index); - if (ch == -1) - break; - - switch (ch) { - case OptHelp: - usage(); - return 0; - case OptGetReg: - is_get++; - break; - case OptSetReg: - is_set++; - reg_set = optarg; - - break; - case '?': - fprintf(stderr, "Unknown argument `%s'\n", - argv[optind]); - usage(); - return 1; - } - } - if (optind < argc) { - printf("unknown arguments: "); - while (optind < argc) - printf("%s ", argv[optind++]); - printf("\n"); - usage(); - return 1; - } - - fd = open(device, O_RDWR); - if (fd < 0) { - fprintf(stderr, "Failed to open %s: %s\n", device, - strerror(errno)); - exit(1); - } - free(device); - - if (ioctl(fd, VIDIOC_QUERYCAP, &cap) < 0) { - printf("Error while reading capabilities\n"); - exit(2); - } - - for (board = ARRAY_SIZE(boards)-1; board >= 0; board--) { - if (!strcasecmp((char *)cap.driver, boards[board].name)) - break; - } - if (board < 0) { - printf("This software doesn't support %s yet\n", cap.driver); - exit(3); - } - - curr_bd = &boards[board]; - - reg.match_type = V4L2_CHIP_MATCH_HOST; - reg.match_chip = 0; - - if (is_get) { - for (i = 0; i < curr_bd->regs_size; i++) { - char name[256]; - reg.reg = curr_bd->regs[i].reg; - if (ioctl(fd, VIDIOC_DBG_G_REGISTER, ®) < 0) { - printf("Error while reading. Maybe you're not root?\n"); - continue; - } - sprintf(name, "%s:", curr_bd->regs[i].name); - - switch (curr_bd->regs[i].size) { - case 1: - printf("%-32s %02llx ", name, reg.val & 0xff); - break; - case 2: - printf("%-32s %04llx ", name, reg.val & 0xffff); - break; - case 4: - printf("%-32s %08llx ", name, reg.val & 0xffffffff); - break; - } - print_bin (reg.val, curr_bd->regs[i].size); - printf("\n"); - } - return 0; - } - - if (is_set) { - char *reg_name; - int val; - int r, size; - unsigned prev; - struct board_regs *bd_reg; - - reg_name = strtok(reg_set, "=:"); - val = strtol(strtok(NULL, "=:"), 0L, 0); - - if (!reg_name) { - printf("set argument is invalid\n"); - return -1; - } - - for (i = curr_bd->regs_size - 1; i >=0 ; i--) { - if (!strcasecmp(reg_name, curr_bd->regs[i].name)) { - bd_reg = &curr_bd->regs[i]; - r = bd_reg->reg; - size = bd_reg->size; - break; - } - } - - if (i < 0) { - for (i = curr_bd->alt_regs_size - 1; i >=0 ; i--) { - if (!strcasecmp(reg_name, curr_bd->alt_regs[i].name)) { - bd_reg = &curr_bd->alt_regs[i]; - r = bd_reg->reg; - size = bd_reg->size; - break; - } - } - } - - if (i < 0) { - for (i = curr_bd->regs_size - 1; i >=0 ; i--) { - if (!strcasecmp(reg_name, curr_bd->regs[i].name + curr_bd->prefix)) { - bd_reg = &curr_bd->regs[i]; - r = bd_reg->reg; - size = bd_reg->size; - break; - } - } - } - - if (i < 0) { - for (i = curr_bd->alt_regs_size - 1; i >=0 ; i--) { - if (!strcasecmp(reg_name, curr_bd->alt_regs[i].name + curr_bd->prefix)) { - bd_reg = &curr_bd->regs[i]; - r = bd_reg->reg; - size = bd_reg->size; - break; - } - } - } - - if (i < 0) { - printf("Register not found\n"); - return -1; - } - - reg.reg = r; - if (ioctl(fd, VIDIOC_DBG_G_REGISTER, ®) < 0) { - printf("Error while reading register 0x%02x\n", r); - return -1; - } - prev = reg.val; - - switch (size) { - case 1: - reg.val = (reg.val & (~0xff)) | val; - break; - case 2: - reg.val = (reg.val & (~0xffff)) | val; - break; - case 4: - reg.val = val; - break; - } - - printf("Changing value of register %s(0x%x) from 0x%02x to 0x%02x\n", - bd_reg->name, r, prev, (unsigned int)reg.val); - - prev = reg.val; - - if (ioctl(fd, VIDIOC_DBG_S_REGISTER, ®) < 0) { - printf("Error while writing\n"); - return -1; - } - if (ioctl(fd, VIDIOC_DBG_G_REGISTER, ®) < 0) { - printf("Error while reading register 0x%02x\n", r); - return -1; - } - if (reg.val != prev) { - printf("Value of register %s(0x%x) is now 0x%02x\n", - bd_reg->name, r, (unsigned int)reg.val); - } - } - - close(fd); - exit(0); -} diff --git a/v4l2-apps/util/v4l2-compliance.cpp b/v4l2-apps/util/v4l2-compliance.cpp new file mode 100644 index 000000000..2fe5292fe --- /dev/null +++ b/v4l2-apps/util/v4l2-compliance.cpp @@ -0,0 +1,1122 @@ +/* + V4L2 API compliance test tool. + + Copyright (C) 2008 Hans Verkuil <hverkuil@xs4all.nl> + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <unistd.h> +#include <features.h> /* Uses _GNU_SOURCE to define getsubopt in stdlib.h */ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <inttypes.h> +#include <getopt.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <ctype.h> +#include <errno.h> +#include <sys/ioctl.h> +#include <sys/time.h> +#include <math.h> +#include <sys/klog.h> + +#include <linux/videodev2.h> + +#include <list> +#include <vector> +#include <map> +#include <string> + +/* Short option list + + Please keep in alphabetical order. + That makes it easier to see which short options are still free. + + In general the lower case is used to set something and the upper + case is used to retrieve a setting. */ +enum Option { + OptSetDevice = 'd', + OptGetDriverInfo = 'D', + OptHelp = 'h', + OptTest = 't', + OptVerbose = 'v', + OptLast = 256 +}; + +enum Test { + TestCap = 0, + TestChipIdent, + TestRegister, + TestLogStatus, + TestMax +}; + +static int test[TestMax]; + +static char options[OptLast]; + +static int app_result; +static int verbose; + +static unsigned caps; + +typedef std::vector<struct v4l2_ext_control> ctrl_list; +static ctrl_list user_ctrls; +static ctrl_list mpeg_ctrls; + +typedef std::map<std::string, unsigned> ctrl_strmap; +static ctrl_strmap ctrl_str2id; +typedef std::map<unsigned, std::string> ctrl_idmap; +static ctrl_idmap ctrl_id2str; + +typedef std::list<std::string> ctrl_get_list; +static ctrl_get_list get_ctrls; + +typedef std::map<std::string,std::string> ctrl_set_map; +static ctrl_set_map set_ctrls; + +typedef struct { + unsigned flag; + const char *str; +} flag_def; + +static const flag_def service_def[] = { + { V4L2_SLICED_TELETEXT_B, "teletext" }, + { V4L2_SLICED_VPS, "vps" }, + { V4L2_SLICED_CAPTION_525, "cc" }, + { V4L2_SLICED_WSS_625, "wss" }, + { 0, NULL } +}; + +/* fmts specified */ +#define FmtWidth (1L<<0) +#define FmtHeight (1L<<1) +#define FmtChromaKey (1L<<2) +#define FmtGlobalAlpha (1L<<3) + +/* crop specified */ +#define CropWidth (1L<<0) +#define CropHeight (1L<<1) +#define CropLeft (1L<<2) +#define CropTop (1L<<3) + +static struct option long_options[] = { + {"device", required_argument, 0, OptSetDevice}, + {"help", no_argument, 0, OptHelp}, + {"info", no_argument, 0, OptGetDriverInfo}, + {"verbose", no_argument, 0, OptVerbose}, + {"test", required_argument, 0, OptTest}, + {0, 0, 0, 0} +}; + +static void usage(void) +{ + printf("Usage:\n"); + printf("Common options:\n"); + printf(" -D, --info show driver info [VIDIOC_QUERYCAP]\n"); + printf(" -d, --device=<dev> use device <dev> instead of /dev/video0\n"); + printf(" if <dev> is a single digit, then /dev/video<dev> is used\n"); + printf(" -h, --help display this help message\n"); + printf(" -t, --test=<num> run specified test.\n"); + printf(" By default all tests are run.\n"); + printf(" 0 = test VIDIOC_QUERYCAP\n"); + printf(" -v, --verbose turn on verbose ioctl error reporting.\n"); + exit(0); +} + +static std::string num2s(unsigned num) +{ + char buf[10]; + + sprintf(buf, "%08x", num); + return buf; +} + +static std::string buftype2s(int type) +{ + switch (type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + return "Video Capture"; + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + return "Video Output"; + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + return "Video Overlay"; + case V4L2_BUF_TYPE_VBI_CAPTURE: + return "VBI Capture"; + case V4L2_BUF_TYPE_VBI_OUTPUT: + return "VBI Output"; + case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: + return "Sliced VBI Capture"; + case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: + return "Sliced VBI Output"; + case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: + return "Video Output Overlay"; + case V4L2_BUF_TYPE_PRIVATE: + return "Private"; + default: + return "Unknown (" + num2s(type) + ")"; + } +} + +static std::string fcc2s(unsigned int val) +{ + std::string s; + + s += val & 0xff; + s += (val >> 8) & 0xff; + s += (val >> 16) & 0xff; + s += (val >> 24) & 0xff; + return s; +} + +static std::string field2s(int val) +{ + switch (val) { + case V4L2_FIELD_ANY: + return "Any"; + case V4L2_FIELD_NONE: + return "None"; + case V4L2_FIELD_TOP: + return "Top"; + case V4L2_FIELD_BOTTOM: + return "Bottom"; + case V4L2_FIELD_INTERLACED: + return "Interlaced"; + case V4L2_FIELD_SEQ_TB: + return "Sequential Top-Bottom"; + case V4L2_FIELD_SEQ_BT: + return "Sequential Bottom-Top"; + case V4L2_FIELD_ALTERNATE: + return "Alternating"; + case V4L2_FIELD_INTERLACED_TB: + return "Interlaced Top-Bottom"; + case V4L2_FIELD_INTERLACED_BT: + return "Interlaced Bottom-Top"; + default: + return "Unknown (" + num2s(val) + ")"; + } +} + +static std::string colorspace2s(int val) +{ + switch (val) { + case V4L2_COLORSPACE_SMPTE170M: + return "Broadcast NTSC/PAL (SMPTE170M/ITU601)"; + case V4L2_COLORSPACE_SMPTE240M: + return "1125-Line (US) HDTV (SMPTE240M)"; + case V4L2_COLORSPACE_REC709: + return "HDTV and modern devices (ITU709)"; + case V4L2_COLORSPACE_BT878: + return "Broken Bt878"; + case V4L2_COLORSPACE_470_SYSTEM_M: + return "NTSC/M (ITU470/ITU601)"; + case V4L2_COLORSPACE_470_SYSTEM_BG: + return "PAL/SECAM BG (ITU470/ITU601)"; + case V4L2_COLORSPACE_JPEG: + return "JPEG (JFIF/ITU601)"; + case V4L2_COLORSPACE_SRGB: + return "SRGB"; + default: + return "Unknown (" + num2s(val) + ")"; + } +} + +static std::string flags2s(unsigned val, const flag_def *def) +{ + std::string s; + + while (def->flag) { + if (val & def->flag) { + if (s.length()) s += " "; + s += def->str; + } + def++; + } + return s; +} + +static void print_sliced_vbi_cap(struct v4l2_sliced_vbi_cap &cap) +{ + printf("\tType : %s\n", buftype2s(cap.type).c_str()); + printf("\tService Set : %s\n", + flags2s(cap.service_set, service_def).c_str()); + for (int i = 0; i < 24; i++) { + printf("\tService Line %2d: %8s / %-8s\n", i, + flags2s(cap.service_lines[0][i], service_def).c_str(), + flags2s(cap.service_lines[1][i], service_def).c_str()); + } +} + +static std::string name2var(unsigned char *name) +{ + std::string s; + + while (*name) { + if (*name == ' ') s += "_"; + else s += std::string(1, tolower(*name)); + name++; + } + return s; +} + +static void print_qctrl(int fd, struct v4l2_queryctrl *queryctrl, + struct v4l2_ext_control *ctrl, int show_menus) +{ + struct v4l2_querymenu qmenu = { 0 }; + std::string s = name2var(queryctrl->name); + int i; + + qmenu.id = queryctrl->id; + switch (queryctrl->type) { + case V4L2_CTRL_TYPE_INTEGER: + printf("%31s (int) : min=%d max=%d step=%d default=%d value=%d", + s.c_str(), + queryctrl->minimum, queryctrl->maximum, + queryctrl->step, queryctrl->default_value, + ctrl->value); + break; + case V4L2_CTRL_TYPE_INTEGER64: + printf("%31s (int64): value=%lld", s.c_str(), ctrl->value64); + break; + case V4L2_CTRL_TYPE_BOOLEAN: + printf("%31s (bool) : default=%d value=%d", + s.c_str(), + queryctrl->default_value, ctrl->value); + break; + case V4L2_CTRL_TYPE_MENU: + printf("%31s (menu) : min=%d max=%d default=%d value=%d", + s.c_str(), + queryctrl->minimum, queryctrl->maximum, + queryctrl->default_value, ctrl->value); + break; + case V4L2_CTRL_TYPE_BUTTON: + printf("%31s (button)\n", s.c_str()); + break; + default: break; + } + if (queryctrl->flags) { + const flag_def def[] = { + { V4L2_CTRL_FLAG_GRABBED, "grabbed" }, + { V4L2_CTRL_FLAG_READ_ONLY, "readonly" }, + { V4L2_CTRL_FLAG_UPDATE, "update" }, + { V4L2_CTRL_FLAG_INACTIVE, "inactive" }, + { V4L2_CTRL_FLAG_SLIDER, "slider" }, + { 0, NULL } + }; + printf(" flags=%s", flags2s(queryctrl->flags, def).c_str()); + } + printf("\n"); + if (queryctrl->type == V4L2_CTRL_TYPE_MENU && show_menus) { + for (i = 0; i <= queryctrl->maximum; i++) { + qmenu.index = i; + if (ioctl(fd, VIDIOC_QUERYMENU, &qmenu)) + continue; + printf("\t\t\t\t%d: %s\n", i, qmenu.name); + } + } +} + +static int print_control(int fd, struct v4l2_queryctrl &qctrl, int show_menus) +{ + struct v4l2_control ctrl = { 0 }; + struct v4l2_ext_control ext_ctrl = { 0 }; + struct v4l2_ext_controls ctrls = { 0 }; + + if (qctrl.flags & V4L2_CTRL_FLAG_DISABLED) + return 1; + if (qctrl.type == V4L2_CTRL_TYPE_CTRL_CLASS) { + printf("\n%s\n\n", qctrl.name); + return 1; + } + ext_ctrl.id = qctrl.id; + ctrls.ctrl_class = V4L2_CTRL_ID2CLASS(qctrl.id); + ctrls.count = 1; + ctrls.controls = &ext_ctrl; + if (V4L2_CTRL_ID2CLASS(qctrl.id) != V4L2_CTRL_CLASS_USER && + qctrl.id < V4L2_CID_PRIVATE_BASE) { + if (ioctl(fd, VIDIOC_G_EXT_CTRLS, &ctrls)) { + printf("error %d getting ext_ctrl %s\n", + errno, qctrl.name); + return 0; + } + } + else { + ctrl.id = qctrl.id; + if (ioctl(fd, VIDIOC_G_CTRL, &ctrl)) { + printf("error %d getting ctrl %s\n", + errno, qctrl.name); + return 0; + } + ext_ctrl.value = ctrl.value; + } + print_qctrl(fd, &qctrl, &ext_ctrl, show_menus); + return 1; +} + +static void list_controls(int fd, int show_menus) +{ + struct v4l2_queryctrl qctrl = { V4L2_CTRL_FLAG_NEXT_CTRL }; + int id; + + while (ioctl(fd, VIDIOC_QUERYCTRL, &qctrl) == 0) { + print_control(fd, qctrl, show_menus); + qctrl.id |= V4L2_CTRL_FLAG_NEXT_CTRL; + } + if (qctrl.id != V4L2_CTRL_FLAG_NEXT_CTRL) + return; + for (id = V4L2_CID_USER_BASE; id < V4L2_CID_LASTP1; id++) { + qctrl.id = id; + if (ioctl(fd, VIDIOC_QUERYCTRL, &qctrl) == 0) + print_control(fd, qctrl, show_menus); + } + for (qctrl.id = V4L2_CID_PRIVATE_BASE; + ioctl(fd, VIDIOC_QUERYCTRL, &qctrl) == 0; qctrl.id++) { + print_control(fd, qctrl, show_menus); + } +} + +static void find_controls(int fd) +{ + struct v4l2_queryctrl qctrl = { V4L2_CTRL_FLAG_NEXT_CTRL }; + int id; + + while (ioctl(fd, VIDIOC_QUERYCTRL, &qctrl) == 0) { + if (qctrl.type != V4L2_CTRL_TYPE_CTRL_CLASS && + !(qctrl.flags & V4L2_CTRL_FLAG_DISABLED)) { + ctrl_str2id[name2var(qctrl.name)] = qctrl.id; + ctrl_id2str[qctrl.id] = name2var(qctrl.name); + } + qctrl.id |= V4L2_CTRL_FLAG_NEXT_CTRL; + } + if (qctrl.id != V4L2_CTRL_FLAG_NEXT_CTRL) + return; + for (id = V4L2_CID_USER_BASE; id < V4L2_CID_LASTP1; id++) { + qctrl.id = id; + if (ioctl(fd, VIDIOC_QUERYCTRL, &qctrl) == 0 && + !(qctrl.flags & V4L2_CTRL_FLAG_DISABLED)) + ctrl_str2id[name2var(qctrl.name)] = qctrl.id; + } + for (qctrl.id = V4L2_CID_PRIVATE_BASE; + ioctl(fd, VIDIOC_QUERYCTRL, &qctrl) == 0; qctrl.id++) { + if (!(qctrl.flags & V4L2_CTRL_FLAG_DISABLED)) + ctrl_str2id[name2var(qctrl.name)] = qctrl.id; + } +} + +static std::string fbufcap2s(unsigned cap) +{ + std::string s; + + if (cap & V4L2_FBUF_CAP_EXTERNOVERLAY) + s += "\t\t\tExtern Overlay\n"; + if (cap & V4L2_FBUF_CAP_CHROMAKEY) + s += "\t\t\tChromakey\n"; + if (cap & V4L2_FBUF_CAP_GLOBAL_ALPHA) + s += "\t\t\tGlobal Alpha\n"; + if (cap & V4L2_FBUF_CAP_LOCAL_ALPHA) + s += "\t\t\tLocal Alpha\n"; + if (cap & V4L2_FBUF_CAP_LOCAL_INV_ALPHA) + s += "\t\t\tLocal Inverted Alpha\n"; + if (cap & V4L2_FBUF_CAP_LIST_CLIPPING) + s += "\t\t\tClipping List\n"; + if (cap & V4L2_FBUF_CAP_BITMAP_CLIPPING) + s += "\t\t\tClipping Bitmap\n"; + if (s.empty()) s += "\t\t\t\n"; + return s; +} + +static std::string fbufflags2s(unsigned fl) +{ + std::string s; + + if (fl & V4L2_FBUF_FLAG_PRIMARY) + s += "\t\t\tPrimary Graphics Surface\n"; + if (fl & V4L2_FBUF_FLAG_OVERLAY) + s += "\t\t\tOverlay Matches Capture/Output Size\n"; + if (fl & V4L2_FBUF_FLAG_CHROMAKEY) + s += "\t\t\tChromakey\n"; + if (fl & V4L2_FBUF_FLAG_GLOBAL_ALPHA) + s += "\t\t\tGlobal Alpha\n"; + if (fl & V4L2_FBUF_FLAG_LOCAL_ALPHA) + s += "\t\t\tLocal Alpha\n"; + if (fl & V4L2_FBUF_FLAG_LOCAL_INV_ALPHA) + s += "\t\t\tLocal Inverted Alpha\n"; + if (s.empty()) s += "\t\t\t\n"; + return s; +} + +static void printfbuf(const struct v4l2_framebuffer &fb) +{ + int is_ext = fb.capability & V4L2_FBUF_CAP_EXTERNOVERLAY; + + printf("Framebuffer Format:\n"); + printf("\tCapability : %s", fbufcap2s(fb.capability).c_str() + 3); + printf("\tFlags : %s", fbufflags2s(fb.flags).c_str() + 3); + if (fb.base) + printf("\tBase : 0x%p\n", fb.base); + printf("\tWidth : %d\n", fb.fmt.width); + printf("\tHeight : %d\n", fb.fmt.height); + printf("\tPixel Format : %s\n", fcc2s(fb.fmt.pixelformat).c_str()); + if (!is_ext) { + printf("\tBytes per Line: %d\n", fb.fmt.bytesperline); + printf("\tSize image : %d\n", fb.fmt.sizeimage); + printf("\tColorspace : %s\n", colorspace2s(fb.fmt.colorspace).c_str()); + if (fb.fmt.priv) + printf("\tCustom Info : %08x\n", fb.fmt.priv); + } +} + +static void printcrop(const struct v4l2_crop &crop) +{ + printf("Crop: Left %d, Top %d, Width %d, Height %d\n", + crop.c.left, crop.c.top, crop.c.width, crop.c.height); +} + +static void printcropcap(const struct v4l2_cropcap &cropcap) +{ + printf("Crop Capability %s:\n", buftype2s(cropcap.type).c_str()); + printf("\tBounds : Left %d, Top %d, Width %d, Height %d\n", + cropcap.bounds.left, cropcap.bounds.top, cropcap.bounds.width, cropcap.bounds.height); + printf("\tDefault : Left %d, Top %d, Width %d, Height %d\n", + cropcap.defrect.left, cropcap.defrect.top, cropcap.defrect.width, cropcap.defrect.height); + printf("\tPixel Aspect: %u/%u\n", cropcap.pixelaspect.numerator, cropcap.pixelaspect.denominator); +} + +static void printfmt(struct v4l2_format vfmt) +{ + const flag_def vbi_def[] = { + { V4L2_VBI_UNSYNC, "unsynchronized" }, + { V4L2_VBI_INTERLACED, "interlaced" }, + { 0, NULL } + }; + printf("Format %s:\n", buftype2s(vfmt.type).c_str()); + + switch (vfmt.type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + printf("\tWidth/Height : %u/%u\n", vfmt.fmt.pix.width, vfmt.fmt.pix.height); + printf("\tPixel Format : %s\n", fcc2s(vfmt.fmt.pix.pixelformat).c_str()); + printf("\tField : %s\n", field2s(vfmt.fmt.pix.field).c_str()); + printf("\tBytes per Line: %u\n", vfmt.fmt.pix.bytesperline); + printf("\tSize Image : %u\n", vfmt.fmt.pix.sizeimage); + printf("\tColorspace : %s\n", colorspace2s(vfmt.fmt.pix.colorspace).c_str()); + if (vfmt.fmt.pix.priv) + printf("\tCustom Info : %08x\n", vfmt.fmt.pix.priv); + break; + case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + printf("\tLeft/Top : %d/%d\n", + vfmt.fmt.win.w.left, vfmt.fmt.win.w.top); + printf("\tWidth/Height: %d/%d\n", + vfmt.fmt.win.w.width, vfmt.fmt.win.w.height); + printf("\tField : %s\n", field2s(vfmt.fmt.win.field).c_str()); + printf("\tChroma Key : 0x%08x\n", vfmt.fmt.win.chromakey); + printf("\tGlobal Alpha: 0x%02x\n", vfmt.fmt.win.global_alpha); + printf("\tClip Count : %u\n", vfmt.fmt.win.clipcount); + printf("\tClip Bitmap : %s\n", vfmt.fmt.win.bitmap ? "Yes" : "No"); + break; + case V4L2_BUF_TYPE_VBI_CAPTURE: + case V4L2_BUF_TYPE_VBI_OUTPUT: + printf("\tSampling Rate : %u Hz\n", vfmt.fmt.vbi.sampling_rate); + printf("\tOffset : %u samples (%g secs after leading edge)\n", + vfmt.fmt.vbi.offset, + (double)vfmt.fmt.vbi.offset / (double)vfmt.fmt.vbi.sampling_rate); + printf("\tSamples per Line: %u\n", vfmt.fmt.vbi.samples_per_line); + printf("\tSample Format : %s\n", fcc2s(vfmt.fmt.vbi.sample_format).c_str()); + printf("\tStart 1st Field : %u\n", vfmt.fmt.vbi.start[0]); + printf("\tCount 1st Field : %u\n", vfmt.fmt.vbi.count[0]); + printf("\tStart 2nd Field : %u\n", vfmt.fmt.vbi.start[1]); + printf("\tCount 2nd Field : %u\n", vfmt.fmt.vbi.count[1]); + if (vfmt.fmt.vbi.flags) + printf("\tFlags : %s\n", flags2s(vfmt.fmt.vbi.flags, vbi_def).c_str()); + break; + case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: + case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: + printf("\tService Set : %s\n", + flags2s(vfmt.fmt.sliced.service_set, service_def).c_str()); + for (int i = 0; i < 24; i++) { + printf("\tService Line %2d: %8s / %-8s\n", i, + flags2s(vfmt.fmt.sliced.service_lines[0][i], service_def).c_str(), + flags2s(vfmt.fmt.sliced.service_lines[1][i], service_def).c_str()); + } + printf("\tI/O Size : %u\n", vfmt.fmt.sliced.io_size); + break; + case V4L2_BUF_TYPE_PRIVATE: + break; + } +} + +static void print_video_formats(int fd, enum v4l2_buf_type type) +{ + struct v4l2_fmtdesc fmt; + + fmt.index = 0; + fmt.type = type; + while (ioctl(fd, VIDIOC_ENUM_FMT, &fmt) >= 0) { + printf("\tType : %s\n", buftype2s(type).c_str()); + printf("\tPixelformat : %s", fcc2s(fmt.pixelformat).c_str()); + if (fmt.flags) + printf(" (compressed)"); + printf("\n"); + printf("\tName : %s\n", fmt.description); + printf("\n"); + fmt.index++; + } +} + +static char *pts_to_string(char *str, unsigned long pts) +{ + static char buf[256]; + int hours, minutes, seconds, fracsec; + float fps; + int frame; + char *p = (str) ? str : buf; + + static const int MPEG_CLOCK_FREQ = 90000; + seconds = pts / MPEG_CLOCK_FREQ; + fracsec = pts % MPEG_CLOCK_FREQ; + + minutes = seconds / 60; + seconds = seconds % 60; + + hours = minutes / 60; + minutes = minutes % 60; + + fps = 30; + frame = (int)ceilf(((float)fracsec / (float)MPEG_CLOCK_FREQ) * fps); + + snprintf(p, sizeof(buf), "%d:%02d:%02d:%d", hours, minutes, seconds, + frame); + return p; +} + +static const char *audmode2s(int audmode) +{ + switch (audmode) { + case V4L2_TUNER_MODE_STEREO: return "stereo"; + case V4L2_TUNER_MODE_LANG1: return "lang1"; + case V4L2_TUNER_MODE_LANG2: return "lang2"; + case V4L2_TUNER_MODE_LANG1_LANG2: return "bilingual"; + case V4L2_TUNER_MODE_MONO: return "mono"; + default: return "unknown"; + } +} + +static std::string rxsubchans2s(int rxsubchans) +{ + std::string s; + + if (rxsubchans & V4L2_TUNER_SUB_MONO) + s += "mono "; + if (rxsubchans & V4L2_TUNER_SUB_STEREO) + s += "stereo "; + if (rxsubchans & V4L2_TUNER_SUB_LANG1) + s += "lang1 "; + if (rxsubchans & V4L2_TUNER_SUB_LANG2) + s += "lang2 "; + return s; +} + +static std::string tcap2s(unsigned cap) +{ + std::string s; + + if (cap & V4L2_TUNER_CAP_LOW) + s += "62.5 Hz "; + else + s += "62.5 kHz "; + if (cap & V4L2_TUNER_CAP_NORM) + s += "multi-standard "; + if (cap & V4L2_TUNER_CAP_STEREO) + s += "stereo "; + if (cap & V4L2_TUNER_CAP_LANG1) + s += "lang1 "; + if (cap & V4L2_TUNER_CAP_LANG2) + s += "lang2 "; + return s; +} + +static std::string cap2s(unsigned cap) +{ + std::string s; + + if (cap & V4L2_CAP_VIDEO_CAPTURE) + s += "\t\tVideo Capture\n"; + if (cap & V4L2_CAP_VIDEO_OUTPUT) + s += "\t\tVideo Output\n"; + if (cap & V4L2_CAP_VIDEO_OVERLAY) + s += "\t\tVideo Overlay\n"; + if (cap & V4L2_CAP_VIDEO_OUTPUT_OVERLAY) + s += "\t\tVideo Output Overlay\n"; + if (cap & V4L2_CAP_VBI_CAPTURE) + s += "\t\tVBI Capture\n"; + if (cap & V4L2_CAP_VBI_OUTPUT) + s += "\t\tVBI Output\n"; + if (cap & V4L2_CAP_SLICED_VBI_CAPTURE) + s += "\t\tSliced VBI Capture\n"; + if (cap & V4L2_CAP_SLICED_VBI_OUTPUT) + s += "\t\tSliced VBI Output\n"; + if (cap & V4L2_CAP_RDS_CAPTURE) + s += "\t\tRDS Capture\n"; + if (cap & V4L2_CAP_TUNER) + s += "\t\tTuner\n"; + if (cap & V4L2_CAP_AUDIO) + s += "\t\tAudio\n"; + if (cap & V4L2_CAP_RADIO) + s += "\t\tRadio\n"; + if (cap & V4L2_CAP_READWRITE) + s += "\t\tRead/Write\n"; + if (cap & V4L2_CAP_ASYNCIO) + s += "\t\tAsync I/O\n"; + if (cap & V4L2_CAP_STREAMING) + s += "\t\tStreaming\n"; + return s; +} + +static v4l2_std_id parse_pal(const char *pal) +{ + if (pal[0] == '-') { + switch (pal[1]) { + case '6': + return V4L2_STD_PAL_60; + case 'b': + case 'B': + case 'g': + case 'G': + return V4L2_STD_PAL_BG; + case 'h': + case 'H': + return V4L2_STD_PAL_H; + case 'n': + case 'N': + if (pal[2] == 'c' || pal[2] == 'C') + return V4L2_STD_PAL_Nc; + return V4L2_STD_PAL_N; + case 'i': + case 'I': + return V4L2_STD_PAL_I; + case 'd': + case 'D': + case 'k': + case 'K': + return V4L2_STD_PAL_DK; + case 'M': + case 'm': + return V4L2_STD_PAL_M; + case '-': + break; + } + } + fprintf(stderr, "pal specifier not recognised\n"); + return 0; +} + +static v4l2_std_id parse_secam(const char *secam) +{ + if (secam[0] == '-') { + switch (secam[1]) { + case 'b': + case 'B': + case 'g': + case 'G': + case 'h': + case 'H': + return V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H; + case 'd': + case 'D': + case 'k': + case 'K': + return V4L2_STD_SECAM_DK; + case 'l': + case 'L': + if (secam[2] == 'C' || secam[2] == 'c') + return V4L2_STD_SECAM_LC; + return V4L2_STD_SECAM_L; + case '-': + break; + } + } + fprintf(stderr, "secam specifier not recognised\n"); + return 0; +} + +static v4l2_std_id parse_ntsc(const char *ntsc) +{ + if (ntsc[0] == '-') { + switch (ntsc[1]) { + case 'm': + case 'M': + return V4L2_STD_NTSC_M; + case 'j': + case 'J': + return V4L2_STD_NTSC_M_JP; + case 'k': + case 'K': + return V4L2_STD_NTSC_M_KR; + case '-': + break; + } + } + fprintf(stderr, "ntsc specifier not recognised\n"); + return 0; +} + +static int doioctl(int fd, int request, void *parm, const char *name) +{ + int retVal; + int e; + + errno = 0; + retVal = ioctl(fd, request, parm); + e = errno; + if (verbose) + printf("\t\t%s returned %d (%s)\n", name, retVal, strerror(e)); + if (retVal == 0) return retVal; + if (retVal != -1) { + return -1; + } + retVal = e; + return retVal; +} + +static int parse_subopt(char **subs, char * const *subopts, char **value) +{ + int opt = getsubopt(subs, subopts, value); + + if (opt == -1) { + fprintf(stderr, "Invalid suboptions specified\n"); + usage(); + exit(1); + } + if (value == NULL) { + fprintf(stderr, "No value given to suboption <%s>\n", + subopts[opt]); + usage(); + exit(1); + } + return opt; +} + +static void parse_next_subopt(char **subs, char **value) +{ + static char *const subopts[] = { + NULL + }; + int opt = getsubopt(subs, subopts, value); + + if (value == NULL) { + fprintf(stderr, "No value given to suboption <%s>\n", + subopts[opt]); + usage(); + exit(1); + } +} + +static void print_std(const char *prefix, const char *stds[], unsigned long long std) +{ + int first = 1; + + printf("\t%s-", prefix); + while (*stds) { + if (std & 1) { + if (!first) + printf("/"); + first = 0; + printf("%s", *stds); + } + stds++; + std >>= 1; + } + printf("\n"); +} + +static const char *ok(int res) +{ + if (res) + app_result = res; + return res ? "FAIL" : "OK"; +} + +static int check_string(const char *s, int len, const char *fld) +{ + if (strlen(s) == 0) { + if (verbose) + printf("%s field empty\n", fld); + return -1; + } + if (strlen(s) >= len) { + if (verbose) + printf("%s field not 0-terminated\n", fld); + return -1; + } + return 0; +} + +static int check_ustring(const __u8 *s, int len, const char *fld) +{ + return check_string((const char *)s, len, fld); +} + +static int check_0(void *p, int len) +{ + __u8 *q = (__u8 *)p; + + while (len--) + if (*q++) { + if (verbose) + printf("array not zeroed by driver\n"); + return -1; + } + return 0; +} + +static int testCap(int fd) +{ + struct v4l2_capability vcap; + __u32 caps; + + if (doioctl(fd, VIDIOC_QUERYCAP, &vcap, "VIDIOC_QUERYCAP")) + return -1; + if (check_ustring(vcap.driver, sizeof(vcap.driver), "driver")) + return -1; + if (check_ustring(vcap.card, sizeof(vcap.card), "card")) + return -1; + if (check_ustring(vcap.bus_info, sizeof(vcap.bus_info), "bus_info")) + return -1; + if (check_0(vcap.reserved, sizeof(vcap.reserved))) + return -1; + caps = vcap.capabilities; + if (caps == 0) { + if (verbose) printf("no capabilities set\n"); + return -1; + } + return 0; +} + +static int testChipIdent(int fd) +{ + struct v4l2_chip_ident chip; + int ret; + + chip.match_type = V4L2_CHIP_MATCH_HOST; + chip.match_chip = 0; + ret = doioctl(fd, VIDIOC_G_CHIP_IDENT, &chip, "VIDIOC_G_CHIP_IDENT"); + // Must return either 0 (OK) or EINVAL (not supported) + if (ret == 0) { + struct v4l2_chip_ident orig; + + // set invalid match_type + chip.match_type = V4L2_CHIP_MATCH_I2C_ADDR + 1; + chip.match_chip = 0xdeadbeef; + chip.ident = 0xdeadbeef; + chip.revision = 0xdeadbeef; + orig = chip; + ret = doioctl(fd, VIDIOC_G_CHIP_IDENT, &chip, "VIDIOC_G_CHIP_IDENT"); + if (ret != EINVAL) { + if (verbose) + printf("Invalid match_type accepted\n"); + return -1; + } + if (memcmp(&orig, &chip, sizeof(chip))) { + if (verbose) + printf("Error, but struct modified\n"); + return -1; + } + return 0; + } + return ret != EINVAL; +} + +static int testRegister(int fd) +{ + struct v4l2_register reg; + struct v4l2_chip_ident chip; + int ret; + int uid = getuid(); + + reg.match_type = V4L2_CHIP_MATCH_HOST; + reg.match_chip = 0; + reg.reg = 0; + ret = doioctl(fd, VIDIOC_DBG_G_REGISTER, ®, "VIDIOC_DBG_G_REGISTER"); + if (ret == EINVAL) + return 0; + if (uid && ret != EPERM) { + printf("Not allowed to call VIDIOC_DBG_G_REGISTER unless root\n"); + return -1; + } + if (uid == 0 && ret) { + printf("Not allowed to call VIDIOC_DBG_G_REGISTER even though we are root\n"); + return -1; + } + chip.match_type = V4L2_CHIP_MATCH_HOST; + chip.match_chip = 0; + if (doioctl(fd, VIDIOC_G_CHIP_IDENT, &chip, "VIDIOC_G_CHIP_IDENT")) { + printf("Must support VIDIOC_G_CHIP_IDENT\n"); + return -1; + } + if (uid) { + // Don't test S_REGISTER as root, don't want to risk + // messing with registers in the compliance test. + reg.reg = reg.val = 0; + ret = doioctl(fd, VIDIOC_DBG_S_REGISTER, ®, "VIDIOC_DBG_S_REGISTER"); + if (ret != EINVAL && ret != EPERM) { + printf("Invalid error calling VIDIOC_DBG_S_REGISTER as non-root\n"); + return -1; + } + } + return 0; +} + +static int testLogStatus(int fd) +{ + int ret = doioctl(fd, VIDIOC_LOG_STATUS, NULL, "VIDIOC_LOG_STATUS"); + + return (ret == 0 || ret == EINVAL) ? 0 : -1; +} + +int main(int argc, char **argv) +{ + char *value, *subs; + int i; + unsigned t; + int fd = -1; + + /* command args */ + int ch; + const char *device = "/dev/video0"; /* -d device */ + struct v4l2_capability vcap; /* list_cap */ + char short_options[26 * 2 * 2 + 1]; + int idx = 0; + int tests = 0; + + for (i = 0; long_options[i].name; i++) { + if (!isalpha(long_options[i].val)) + continue; + short_options[idx++] = long_options[i].val; + if (long_options[i].has_arg == required_argument) + short_options[idx++] = ':'; + } + while (1) { + int option_index = 0; + + short_options[idx] = 0; + ch = getopt_long(argc, argv, short_options, + long_options, &option_index); + if (ch == -1) + break; + + options[(int)ch] = 1; + switch (ch) { + case OptHelp: + usage(); + return 0; + case OptTest: + t = strtoul(optarg, NULL, 0); + + if (t >= TestMax) + usage(); + test[t] = 1; + tests++; + break; + case OptSetDevice: + device = optarg; + if (device[0] >= '0' && device[0] <= '9' && device[1] == 0) { + static char newdev[20]; + char dev = device[0]; + + sprintf(newdev, "/dev/video%c", dev); + device = newdev; + } + break; + case ':': + fprintf(stderr, "Option `%s' requires a value\n", + argv[optind]); + usage(); + return 1; + case '?': + fprintf(stderr, "Unknown argument `%s'\n", + argv[optind]); + usage(); + return 1; + } + } + if (optind < argc) { + printf("unknown arguments: "); + while (optind < argc) + printf("%s ", argv[optind++]); + printf("\n"); + usage(); + return 1; + } + verbose = options[OptVerbose]; + if (!tests) { + for (t = 0; t < TestMax; t++) + test[t] = 1; + } + + if ((fd = open(device, O_RDWR)) < 0) { + fprintf(stderr, "Failed to open %s: %s\n", device, + strerror(errno)); + exit(1); + } + + ioctl(fd, VIDIOC_QUERYCAP, &vcap, "VIDIOC_QUERYCAP"); + caps = vcap.capabilities; + find_controls(fd); + for (ctrl_get_list::iterator iter = get_ctrls.begin(); iter != get_ctrls.end(); ++iter) { + if (ctrl_str2id.find(*iter) == ctrl_str2id.end()) { + fprintf(stderr, "unknown control '%s'\n", (*iter).c_str()); + exit(1); + } + } + for (ctrl_set_map::iterator iter = set_ctrls.begin(); iter != set_ctrls.end(); ++iter) { + if (ctrl_str2id.find(iter->first) == ctrl_str2id.end()) { + fprintf(stderr, "unknown control '%s'\n", iter->first.c_str()); + exit(1); + } + } + + /* Information Opts */ + + if (options[OptGetDriverInfo]) { + printf("Driver Info:\n"); + printf("\tDriver name : %s\n", vcap.driver); + printf("\tCard type : %s\n", vcap.card); + printf("\tBus info : %s\n", vcap.bus_info); + printf("\tDriver version: %d\n", vcap.version); + printf("\tCapabilities : 0x%08X\n", vcap.capabilities); + printf("%s", cap2s(vcap.capabilities).c_str()); + } + + printf("Compliance test for device %s:\n\n", device); + + printf("Required ioctls:\n"); + if (test[TestCap]) + printf("\ttest VIDIOC_QUERYCAP: %s\n", ok(testCap(fd))); + + printf("Debug ioctls:\n"); + if (test[TestChipIdent]) + printf("\ttest VIDIOC_G_CHIP_IDENT: %s\n", ok(testChipIdent(fd))); + if (test[TestRegister]) + printf("\ttest VIDIOC_DBG_G/S_REGISTER: %s\n", ok(testRegister(fd))); + if (test[TestLogStatus]) + printf("\ttest VIDIOC_LOG_STATUS: %s\n", ok(testLogStatus(fd))); + + close(fd); + exit(app_result); +} diff --git a/v4l2-apps/util/v4l2-ctl.cpp b/v4l2-apps/util/v4l2-ctl.cpp index 57d98fc13..417721c57 100644 --- a/v4l2-apps/util/v4l2-ctl.cpp +++ b/v4l2-apps/util/v4l2-ctl.cpp @@ -34,6 +34,7 @@ #include <errno.h> #include <sys/ioctl.h> #include <sys/time.h> +#include <dirent.h> #include <math.h> #include <sys/klog.h> @@ -43,6 +44,7 @@ #include <vector> #include <map> #include <string> +#include <algorithm> /* Short option list @@ -77,15 +79,25 @@ enum Option { OptSetVideoFormat = 'v', OptGetSlicedVbiOutFormat = 128, - OptSetSlicedVbiOutFormat, OptGetOverlayFormat, - //OptSetOverlayFormat, TODO OptGetOutputOverlayFormat, - OptSetOutputOverlayFormat, OptGetVbiFormat, - //OptSetVbiFormat, TODO OptGetVbiOutFormat, + OptGetVideoOutFormat, + OptSetSlicedVbiOutFormat, + OptSetOutputOverlayFormat, + OptSetOverlayFormat, + //OptSetVbiFormat, TODO //OptSetVbiOutFormat, TODO + OptSetVideoOutFormat, + OptTryVideoOutFormat, + OptTrySlicedVbiOutFormat, + OptTrySlicedVbiFormat, + OptTryVideoFormat, + OptTryOutputOverlayFormat, + OptTryOverlayFormat, + //OptTryVbiFormat, TODO + //OptTryVbiOutFormat, TODO OptAll, OptStreamOff, OptStreamOn, @@ -93,8 +105,7 @@ enum Option { OptListFormats, OptLogStatus, OptVerbose, - OptGetVideoOutFormat, - OptSetVideoOutFormat, + OptSilent, OptGetSlicedVbiCap, OptGetSlicedVbiOutCap, OptGetFBuf, @@ -118,12 +129,14 @@ enum Option { OptGetOverlayCropCap, OptGetOutputOverlayCropCap, OptOverlay, + OptListDevices, OptLast = 256 }; static char options[OptLast]; static int app_result; +static int verbose; static unsigned capabilities; @@ -142,6 +155,9 @@ static ctrl_get_list get_ctrls; typedef std::map<std::string,std::string> ctrl_set_map; static ctrl_set_map set_ctrls; +typedef std::vector<std::string> dev_vec; +typedef std::map<std::string, std::string> dev_map; + typedef struct { unsigned flag; const char *str; @@ -160,6 +176,10 @@ static const flag_def service_def[] = { #define FmtHeight (1L<<1) #define FmtChromaKey (1L<<2) #define FmtGlobalAlpha (1L<<3) +#define FmtPixelFormat (1L<<4) +#define FmtLeft (1L<<5) +#define FmtTop (1L<<6) +#define FmtField (1L<<7) /* crop specified */ #define CropWidth (1L<<0) @@ -174,8 +194,10 @@ static struct option long_options[] = { {"device", required_argument, 0, OptSetDevice}, {"get-fmt-video", no_argument, 0, OptGetVideoFormat}, {"set-fmt-video", required_argument, 0, OptSetVideoFormat}, + {"try-fmt-video", required_argument, 0, OptTryVideoFormat}, {"get-fmt-video-out", no_argument, 0, OptGetVideoOutFormat}, {"set-fmt-video-out", required_argument, 0, OptSetVideoOutFormat}, + {"try-fmt-video-out", required_argument, 0, OptTryVideoOutFormat}, {"help", no_argument, 0, OptHelp}, {"get-output", no_argument, 0, OptGetOutput}, {"set-output", required_argument, 0, OptSetOutput}, @@ -205,12 +227,17 @@ static struct option long_options[] = { {"verbose", no_argument, 0, OptVerbose}, {"log-status", no_argument, 0, OptLogStatus}, {"get-fmt-overlay", no_argument, 0, OptGetOverlayFormat}, + {"set-fmt-overlay", required_argument, 0, OptSetOverlayFormat}, + {"try-fmt-overlay", required_argument, 0, OptTryOverlayFormat}, {"get-fmt-output-overlay", no_argument, 0, OptGetOutputOverlayFormat}, {"set-fmt-output-overlay", required_argument, 0, OptSetOutputOverlayFormat}, + {"try-fmt-output-overlay", required_argument, 0, OptTryOutputOverlayFormat}, {"get-fmt-sliced-vbi", no_argument, 0, OptGetSlicedVbiFormat}, {"set-fmt-sliced-vbi", required_argument, 0, OptSetSlicedVbiFormat}, + {"try-fmt-sliced-vbi", required_argument, 0, OptTrySlicedVbiFormat}, {"get-fmt-sliced-vbi-out", no_argument, 0, OptGetSlicedVbiOutFormat}, {"set-fmt-sliced-vbi-out", required_argument, 0, OptSetSlicedVbiOutFormat}, + {"try-fmt-sliced-vbi-out", required_argument, 0, OptTrySlicedVbiOutFormat}, {"get-fmt-vbi", no_argument, 0, OptGetVbiFormat}, {"get-fmt-vbi-out", no_argument, 0, OptGetVbiOutFormat}, {"get-sliced-vbi-cap", no_argument, 0, OptGetSlicedVbiCap}, @@ -230,132 +257,144 @@ static struct option long_options[] = { {"get-crop-output-overlay", no_argument, 0, OptGetOutputOverlayCrop}, {"set-crop-output-overlay", required_argument, 0, OptSetOutputOverlayCrop}, {"overlay", required_argument, 0, OptOverlay}, + {"list-devices", no_argument, 0, OptListDevices}, {0, 0, 0, 0} }; static void usage(void) { printf("Usage:\n"); - printf("Common options:\n"); - printf(" --all display all information available\n"); - printf(" -B, --get-fmt-sliced-vbi\n"); - printf(" query the sliced VBI capture format [VIDIOC_G_FMT]\n"); - printf(" -b, --set-fmt-sliced-vbi=<mode>\n"); - printf(" set the sliced VBI capture format to <mode> [VIDIOC_S_FMT]\n"); - printf(" <mode> is a comma separated list of:\n"); - printf(" off: turn off sliced VBI (cannot be combined with other modes)\n"); - printf(" teletext: teletext (PAL/SECAM)\n"); - printf(" cc: closed caption (NTSC)\n"); - printf(" wss: widescreen signal (PAL/SECAM)\n"); - printf(" vps: VPS (PAL/SECAM)\n"); - printf(" -C, --get-ctrl=<ctrl>[,<ctrl>...]\n"); - printf(" get the value of the controls [VIDIOC_G_EXT_CTRLS]\n"); - printf(" -c, --set-ctrl=<ctrl>=<val>[,<ctrl>=<val>...]\n"); - printf(" set the controls to the values specified [VIDIOC_S_EXT_CTRLS]\n"); - printf(" -D, --info show driver info [VIDIOC_QUERYCAP]\n"); - printf(" -d, --device=<dev> use device <dev> instead of /dev/video0\n"); - printf(" if <dev> is a single digit, then /dev/video<dev> is used\n"); - printf(" -F, --get-freq query the frequency [VIDIOC_G_FREQUENCY]\n"); - printf(" -f, --set-freq=<freq>\n"); - printf(" set the frequency to <freq> MHz [VIDIOC_S_FREQUENCY]\n"); - printf(" -h, --help display this help message\n"); - printf(" -I, --get-input query the video input [VIDIOC_G_INPUT]\n"); - printf(" -i, --set-input=<num>\n"); - printf(" set the video input to <num> [VIDIOC_S_INPUT]\n"); - printf(" -l, --list-ctrls display all controls and their values [VIDIOC_QUERYCTRL]\n"); - printf(" -L, --list-ctrls-menus\n"); - printf(" display all controls, their values and the menus [VIDIOC_QUERYMENU]\n"); - printf(" -N, --list-outputs display video outputs [VIDIOC_ENUMOUTPUT]\n"); - printf(" -n, --list-inputs display video inputs [VIDIOC_ENUMINPUT]\n"); - printf(" -O, --get-output query the video output [VIDIOC_G_OUTPUT]\n"); - printf(" -o, --set-output=<num>\n"); - printf(" set the video output to <num> [VIDIOC_S_OUTPUT]\n"); - printf(" -S, --get-standard\n"); - printf(" query the video standard [VIDIOC_G_STD]\n"); - printf(" -s, --set-standard=<num>\n"); - printf(" set the video standard to <num> [VIDIOC_S_STD]\n"); - printf(" <num> can be a numerical v4l2_std value, or it can be one of:\n"); - printf(" pal-X (X = B/G/H/N/Nc/I/D/K/M/60) or just 'pal' (V4L2_STD_PAL)\n"); - printf(" ntsc-X (X = M/J/K) or just 'ntsc' (V4L2_STD_NTSC)\n"); - printf(" secam-X (X = B/G/H/D/K/L/Lc) or just 'secam' (V4L2_STD_SECAM)\n"); - printf(" --list-standards display supported video standards [VIDIOC_ENUMSTD]\n"); - printf(" -T, --get-tuner query the tuner settings [VIDIOC_G_TUNER]\n"); - printf(" -t, --set-tuner=<mode>\n"); - printf(" set the audio mode of the tuner [VIDIOC_S_TUNER]\n"); - printf(" Possible values: mono, stereo, lang2, lang1, bilingual\n"); - printf(" --list-formats display supported video formats [VIDIOC_ENUM_FMT]\n"); - printf(" -V, --get-fmt-video\n"); - printf(" query the video capture format [VIDIOC_G_FMT]\n"); - printf(" -v, --set-fmt-video=width=<w>,height=<h>\n"); - printf(" set the video capture format [VIDIOC_S_FMT]\n"); - printf(" --verbose turn on verbose ioctl error reporting.\n"); - printf("\n"); - printf("Uncommon options:\n"); - printf(" --get-fmt-video-out\n"); - printf(" query the video output format [VIDIOC_G_FMT]\n"); - printf(" --set-fmt-video-out=width=<w>,height=<h>\n"); - printf(" set the video output format [VIDIOC_S_FMT]\n"); - printf(" --get-fmt-overlay\n"); - printf(" query the video overlay format [VIDIOC_G_FMT]\n"); - printf(" --get-fmt-output-overlay\n"); - printf(" query the video output overlay format [VIDIOC_G_FMT]\n"); - printf(" --set-fmt-output-overlay=chromakey=<key>,global_alpha=<alpha>\n"); - printf(" set the video output overlay format [VIDIOC_S_FMT]\n"); - printf(" --get-sliced-vbi-cap\n"); - printf(" query the sliced VBI capture capabilities [VIDIOC_G_SLICED_VBI_CAP]\n"); - printf(" --get-sliced-vbi-out-cap\n"); - printf(" query the sliced VBI output capabilities [VIDIOC_G_SLICED_VBI_CAP]\n"); - printf(" --get-fmt-sliced-vbi-out\n"); - printf(" query the sliced VBI output format [VIDIOC_G_FMT]\n"); - printf(" --set-fmt-sliced-vbi-out=<mode>\n"); - printf(" set the sliced VBI output format to <mode> [VIDIOC_S_FMT]\n"); - printf(" <mode> is a comma separated list of:\n"); - printf(" off: turn off sliced VBI (cannot be combined with other modes)\n"); - printf(" teletext: teletext (PAL/SECAM)\n"); - printf(" cc: closed caption (NTSC)\n"); - printf(" wss: widescreen signal (PAL/SECAM)\n"); - printf(" vps: VPS (PAL/SECAM)\n"); - printf(" --get-fmt-vbi query the VBI capture format [VIDIOC_G_FMT]\n"); - printf(" --get-fmt-vbi-out query the VBI output format [VIDIOC_G_FMT]\n"); - printf(" --overlay=<on> turn overlay on (1) or off (0) (VIDIOC_OVERLAY)\n"); - printf(" --get-fbuf query the overlay framebuffer data [VIDIOC_G_FBUF]\n"); - printf(" --set-fbuf=chromakey=<0/1>,global_alpha=<0/1>,local_alpha=<0/1>,local_inv_alpha=<0/1>\n"); - printf(" set the overlay framebuffer [VIDIOC_S_FBUF]\n"); - printf(" --get-cropcap query the crop capabilities [VIDIOC_CROPCAP]\n"); - printf(" --get-crop query the video capture crop window [VIDIOC_G_CROP]\n"); - printf(" --set-crop=top=<x>,left=<y>,width=<w>,height=<h>\n"); - printf(" set the video capture crop window [VIDIOC_S_CROP]\n"); - printf(" --get-cropcap-output\n"); - printf(" query the crop capabilities for video output [VIDIOC_CROPCAP]\n"); - printf(" --get-crop-output query the video output crop window [VIDIOC_G_CROP]\n"); - printf(" --set-crop-output=top=<x>,left=<y>,width=<w>,height=<h>\n"); - printf(" set the video output crop window [VIDIOC_S_CROP]\n"); - printf(" --get-cropcap-overlay\n"); - printf(" query the crop capabilities for video overlay [VIDIOC_CROPCAP]\n"); - printf(" --get-crop-overlay query the video overlay crop window [VIDIOC_G_CROP]\n"); - printf(" --set-crop-overlay=top=<x>,left=<y>,width=<w>,height=<h>\n"); - printf(" set the video overlay crop window [VIDIOC_S_CROP]\n"); - printf(" --get-cropcap-output-overlay\n"); - printf(" query the crop capabilities for video output overlays [VIDIOC_CROPCAP]\n"); - printf(" --get-crop-output-overlay\n"); - printf(" query the video output overlay crop window [VIDIOC_G_CROP]\n"); - printf(" --set-crop-output-overlay=top=<x>,left=<y>,width=<w>,height=<h>\n"); - printf(" set the video output overlay crop window [VIDIOC_S_CROP]\n"); - printf(" --get-audio-input query the audio input [VIDIOC_G_AUDIO]\n"); - printf(" --set-audio-input=<num>\n"); - printf(" set the audio input to <num> [VIDIOC_S_AUDIO]\n"); - printf(" --get-audio-output query the audio output [VIDIOC_G_AUDOUT]\n"); - printf(" --set-audio-output=<num>\n"); - printf(" set the audio output to <num> [VIDIOC_S_AUDOUT]\n"); - printf(" --list-audio-outputs\n"); - printf(" display audio outputs [VIDIOC_ENUMAUDOUT]\n"); - printf(" --list-audio-inputs\n"); - printf(" display audio inputs [VIDIOC_ENUMAUDIO]\n"); - printf("\n"); - printf("Expert options:\n"); - printf(" --streamoff turn the stream off [VIDIOC_STREAMOFF]\n"); - printf(" --streamon turn the stream on [VIDIOC_STREAMOFF]\n"); - printf(" --log-status log the board status in the kernel log [VIDIOC_LOG_STATUS]\n"); + printf("Common options:\n" + " --all display all information available\n" + " -C, --get-ctrl=<ctrl>[,<ctrl>...]\n" + " get the value of the controls [VIDIOC_G_EXT_CTRLS]\n" + " -c, --set-ctrl=<ctrl>=<val>[,<ctrl>=<val>...]\n" + " set the controls to the values specified [VIDIOC_S_EXT_CTRLS]\n" + " -D, --info show driver info [VIDIOC_QUERYCAP]\n" + " -d, --device=<dev> use device <dev> instead of /dev/video0\n" + " if <dev> is a single digit, then /dev/video<dev> is used\n" + " -F, --get-freq query the frequency [VIDIOC_G_FREQUENCY]\n" + " -f, --set-freq=<freq>\n" + " set the frequency to <freq> MHz [VIDIOC_S_FREQUENCY]\n" + " -h, --help display this help message\n" + " -I, --get-input query the video input [VIDIOC_G_INPUT]\n" + " -i, --set-input=<num>\n" + " set the video input to <num> [VIDIOC_S_INPUT]\n" + " -l, --list-ctrls display all controls and their values [VIDIOC_QUERYCTRL]\n" + " -L, --list-ctrls-menus\n" + " display all controls, their values and the menus [VIDIOC_QUERYMENU]\n" + " -N, --list-outputs display video outputs [VIDIOC_ENUMOUTPUT]\n" + " -n, --list-inputs display video inputs [VIDIOC_ENUMINPUT]\n" + " -O, --get-output query the video output [VIDIOC_G_OUTPUT]\n" + " -o, --set-output=<num>\n" + " set the video output to <num> [VIDIOC_S_OUTPUT]\n" + " -S, --get-standard\n" + " query the video standard [VIDIOC_G_STD]\n" + " -s, --set-standard=<num>\n" + " set the video standard to <num> [VIDIOC_S_STD]\n" + " <num> can be a numerical v4l2_std value, or it can be one of:\n" + " pal-X (X = B/G/H/N/Nc/I/D/K/M/60) or just 'pal' (V4L2_STD_PAL)\n" + " ntsc-X (X = M/J/K) or just 'ntsc' (V4L2_STD_NTSC)\n" + " secam-X (X = B/G/H/D/K/L/Lc) or just 'secam' (V4L2_STD_SECAM)\n" + " --list-standards display supported video standards [VIDIOC_ENUMSTD]\n" + " -T, --get-tuner query the tuner settings [VIDIOC_G_TUNER]\n" + " -t, --set-tuner=<mode>\n" + " set the audio mode of the tuner [VIDIOC_S_TUNER]\n" + " Possible values: mono, stereo, lang2, lang1, bilingual\n" + " --list-formats display supported video formats [VIDIOC_ENUM_FMT]\n" + " -V, --get-fmt-video\n" + " query the video capture format [VIDIOC_G_FMT]\n" + " -v, --set-fmt-video=width=<w>,height=<h>,pixelformat=<f>\n" + " set the video capture format [VIDIOC_S_FMT]\n" + " pixelformat is either the format index as reported by\n" + " --list-formats, or the fourcc value as a string\n" + " --list-devices list all v4l devices\n" + " --silent only set the result code, do not print any messages\n" + " --verbose turn on verbose ioctl status reporting\n" + "\n"); + printf("Uncommon options:\n" + " --try-fmt-video=width=<w>,height=<h>,pixelformat=<f>\n" + " try the video capture format [VIDIOC_TRY_FMT]\n" + " pixelformat is either the format index as reported by\n" + " --list-formats, or the fourcc value as a string\n" + " --get-fmt-video-out\n" + " query the video output format [VIDIOC_G_FMT]\n" + " --set-fmt-video-out=width=<w>,height=<h>\n" + " set the video output format [VIDIOC_S_FMT]\n" + " --try-fmt-video-out=width=<w>,height=<h>\n" + " try the video output format [VIDIOC_TRY_FMT]\n" + " --get-fmt-overlay query the video overlay format [VIDIOC_G_FMT]\n" + " --get-fmt-output-overlay\n" + " query the video output overlay format [VIDIOC_G_FMT]\n" + " --set-fmt-overlay\n" + " --try-fmt-overlay\n" + " --set-fmt-output-overlay\n" + " --try-fmt-output-overlay=chromakey=<key>,global_alpha=<alpha>,\n" + " top=<t>,left=<l>,width=<w>,height=<h>,field=<f>\n" + " set/try the video or video output overlay format [VIDIOC_TRY_FMT]\n" + " <f> can be one of:\n" + " any, none, top, bottom, interlaced, seq_tb, seq_bt, alternate,\n" + " interlaced_tb, interlaced_bt\n" + " --get-sliced-vbi-cap\n" + " query the sliced VBI capture capabilities [VIDIOC_G_SLICED_VBI_CAP]\n" + " --get-sliced-vbi-out-cap\n" + " query the sliced VBI output capabilities [VIDIOC_G_SLICED_VBI_CAP]\n" + " -B, --get-fmt-sliced-vbi\n" + " query the sliced VBI capture format [VIDIOC_G_FMT]\n" + " --get-fmt-sliced-vbi-out\n" + " query the sliced VBI output format [VIDIOC_G_FMT]\n" + " -b, --set-fmt-sliced-vbi\n" + " --try-fmt-sliced-vbi\n" + " --set-fmt-sliced-vbi-out\n" + " --try-fmt-sliced-vbi-out=<mode>\n" + " (try to) set the sliced VBI capture/output format to <mode> [VIDIOC_S/TRY_FMT]\n" + " <mode> is a comma separated list of:\n" + " off: turn off sliced VBI (cannot be combined with other modes)\n" + " teletext: teletext (PAL/SECAM)\n" + " cc: closed caption (NTSC)\n" + " wss: widescreen signal (PAL/SECAM)\n" + " vps: VPS (PAL/SECAM)\n" + " --get-fmt-vbi query the VBI capture format [VIDIOC_G_FMT]\n" + " --get-fmt-vbi-out query the VBI output format [VIDIOC_G_FMT]\n" + " --overlay=<on> turn overlay on (1) or off (0) (VIDIOC_OVERLAY)\n" + " --get-fbuf query the overlay framebuffer data [VIDIOC_G_FBUF]\n" + " --set-fbuf=chromakey=<0/1>,global_alpha=<0/1>,local_alpha=<0/1>,local_inv_alpha=<0/1>\n" + " set the overlay framebuffer [VIDIOC_S_FBUF]\n" + " --get-cropcap query the crop capabilities [VIDIOC_CROPCAP]\n" + " --get-crop query the video capture crop window [VIDIOC_G_CROP]\n" + " --set-crop=top=<x>,left=<y>,width=<w>,height=<h>\n" + " set the video capture crop window [VIDIOC_S_CROP]\n" + " --get-cropcap-output\n" + " query the crop capabilities for video output [VIDIOC_CROPCAP]\n" + " --get-crop-output query the video output crop window [VIDIOC_G_CROP]\n" + " --set-crop-output=top=<x>,left=<y>,width=<w>,height=<h>\n" + " set the video output crop window [VIDIOC_S_CROP]\n" + " --get-cropcap-overlay\n" + " query the crop capabilities for video overlay [VIDIOC_CROPCAP]\n" + " --get-crop-overlay query the video overlay crop window [VIDIOC_G_CROP]\n" + " --set-crop-overlay=top=<x>,left=<y>,width=<w>,height=<h>\n" + " set the video overlay crop window [VIDIOC_S_CROP]\n" + " --get-cropcap-output-overlay\n" + " query the crop capabilities for video output overlays [VIDIOC_CROPCAP]\n" + " --get-crop-output-overlay\n" + " query the video output overlay crop window [VIDIOC_G_CROP]\n" + " --set-crop-output-overlay=top=<x>,left=<y>,width=<w>,height=<h>\n" + " set the video output overlay crop window [VIDIOC_S_CROP]\n" + " --get-audio-input query the audio input [VIDIOC_G_AUDIO]\n" + " --set-audio-input=<num>\n" + " set the audio input to <num> [VIDIOC_S_AUDIO]\n" + " --get-audio-output query the audio output [VIDIOC_G_AUDOUT]\n" + " --set-audio-output=<num>\n" + " set the audio output to <num> [VIDIOC_S_AUDOUT]\n" + " --list-audio-outputs\n" + " display audio outputs [VIDIOC_ENUMAUDOUT]\n" + " --list-audio-inputs\n" + " display audio inputs [VIDIOC_ENUMAUDIO]\n" + "\n"); + printf("Expert options:\n" + " --streamoff turn the stream off [VIDIOC_STREAMOFF]\n" + " --streamon turn the stream on [VIDIOC_STREAMOFF]\n" + " --log-status log the board status in the kernel log [VIDIOC_LOG_STATUS]\n"); exit(0); } @@ -634,13 +673,17 @@ static void find_controls(int fd) for (id = V4L2_CID_USER_BASE; id < V4L2_CID_LASTP1; id++) { qctrl.id = id; if (ioctl(fd, VIDIOC_QUERYCTRL, &qctrl) == 0 && - !(qctrl.flags & V4L2_CTRL_FLAG_DISABLED)) + !(qctrl.flags & V4L2_CTRL_FLAG_DISABLED)) { ctrl_str2id[name2var(qctrl.name)] = qctrl.id; + ctrl_id2str[qctrl.id] = name2var(qctrl.name); + } } for (qctrl.id = V4L2_CID_PRIVATE_BASE; ioctl(fd, VIDIOC_QUERYCTRL, &qctrl) == 0; qctrl.id++) { - if (!(qctrl.flags & V4L2_CTRL_FLAG_DISABLED)) + if (!(qctrl.flags & V4L2_CTRL_FLAG_DISABLED)) { ctrl_str2id[name2var(qctrl.name)] = qctrl.id; + ctrl_id2str[qctrl.id] = name2var(qctrl.name); + } } } @@ -697,7 +740,7 @@ static void printfbuf(const struct v4l2_framebuffer &fb) printf("\tBase : 0x%p\n", fb.base); printf("\tWidth : %d\n", fb.fmt.width); printf("\tHeight : %d\n", fb.fmt.height); - printf("\tPixel Format : %s\n", fcc2s(fb.fmt.pixelformat).c_str()); + printf("\tPixel Format : '%s'\n", fcc2s(fb.fmt.pixelformat).c_str()); if (!is_ext) { printf("\tBytes per Line: %d\n", fb.fmt.bytesperline); printf("\tSize image : %d\n", fb.fmt.sizeimage); @@ -736,7 +779,7 @@ static void printfmt(struct v4l2_format vfmt) case V4L2_BUF_TYPE_VIDEO_CAPTURE: case V4L2_BUF_TYPE_VIDEO_OUTPUT: printf("\tWidth/Height : %u/%u\n", vfmt.fmt.pix.width, vfmt.fmt.pix.height); - printf("\tPixel Format : %s\n", fcc2s(vfmt.fmt.pix.pixelformat).c_str()); + printf("\tPixel Format : '%s'\n", fcc2s(vfmt.fmt.pix.pixelformat).c_str()); printf("\tField : %s\n", field2s(vfmt.fmt.pix.field).c_str()); printf("\tBytes per Line: %u\n", vfmt.fmt.pix.bytesperline); printf("\tSize Image : %u\n", vfmt.fmt.pix.sizeimage); @@ -794,8 +837,9 @@ static void print_video_formats(int fd, enum v4l2_buf_type type) fmt.index = 0; fmt.type = type; while (ioctl(fd, VIDIOC_ENUM_FMT, &fmt) >= 0) { + printf("\tIndex : %d\n", fmt.index); printf("\tType : %s\n", buftype2s(type).c_str()); - printf("\tPixelformat : %s", fcc2s(fmt.pixelformat).c_str()); + printf("\tPixel Format: '%s'", fcc2s(fmt.pixelformat).c_str()); if (fmt.flags) printf(" (compressed)"); printf("\n"); @@ -1009,19 +1053,97 @@ static int doioctl(int fd, int request, void *parm, const char *name) if (retVal < 0) { app_result = -1; } - if (!options[OptVerbose]) return retVal; - printf("%s: ", name); + if (options[OptSilent]) return retVal; if (retVal < 0) - printf("failed: %s\n", strerror(errno)); - else - printf("ok\n"); + printf("%s: failed: %s\n", name, strerror(errno)); + else if (verbose) + printf("%s: ok\n", name); return retVal; } -static int parse_subopt(char **subs, char * const *subopts, char **value) +static bool is_v4l_dev(const char *name) { - int opt = getsubopt(subs, subopts, value); + return !memcmp(name, "vtx", 3) || + !memcmp(name, "video", 5) || + !memcmp(name, "radio", 5) || + !memcmp(name, "vbi", 3); +} + +static int calc_node_val(const char *s) +{ + int n = 0; + + s = strrchr(s, '/') + 1; + if (!memcmp(s, "video", 5)) n = 0; + else if (!memcmp(s, "radio", 5)) n = 0x100; + else if (!memcmp(s, "vbi", 3)) n = 0x200; + else if (!memcmp(s, "vtx", 3)) n = 0x300; + n += atol(s + (n >= 0x200 ? 3 : 5)); + return n; +} + +static bool sort_on_device_name(const std::string &s1, const std::string &s2) +{ + int n1 = calc_node_val(s1.c_str()); + int n2 = calc_node_val(s2.c_str()); + + return n1 < n2; +} + +static void list_devices() +{ + DIR *dp; + struct dirent *ep; + dev_vec files; + dev_map cards; + struct v4l2_capability vcap; + + dp = opendir("/dev"); + if (dp == NULL) { + perror ("Couldn't open the directory"); + return; + } + while (ep = readdir(dp)) + if (is_v4l_dev(ep->d_name)) + files.push_back(std::string("/dev/") + ep->d_name); + closedir(dp); + +#if 0 + dp = opendir("/dev/v4l"); + if (dp) { + while (ep = readdir(dp)) + if (is_v4l_dev(ep->d_name)) + files.push_back(std::string("/dev/v4l/") + ep->d_name); + closedir(dp); + } +#endif + + std::sort(files.begin(), files.end(), sort_on_device_name); + + for (dev_vec::iterator iter = files.begin(); + iter != files.end(); ++iter) { + int fd = open(iter->c_str(), O_RDWR); + std::string bus_info; + + if (fd < 0) + continue; + doioctl(fd, VIDIOC_QUERYCAP, &vcap, "VIDIOC_QUERYCAP"); + close(fd); + bus_info = (const char *)vcap.bus_info; + if (cards[bus_info].empty()) + cards[bus_info] += std::string((char *)vcap.card) + " (" + bus_info + "):\n"; + cards[bus_info] += "\t" + (*iter) + "\n"; + } + for (dev_map::iterator iter = cards.begin(); + iter != cards.end(); ++iter) { + printf("%s\n", iter->second.c_str()); + } +} + +static int parse_subopt(char **subs, const char * const *subopts, char **value) +{ + int opt = getsubopt(subs, (char * const *)subopts, value); if (opt == -1) { fprintf(stderr, "Invalid suboptions specified\n"); @@ -1094,7 +1216,7 @@ static void parse_crop(char *optarg, unsigned int &set_crop, v4l2_rect &vcrop) char *subs = optarg; while (*subs != '\0') { - static char *const subopts[] = { + static const char *const subopts[] = { "left", "top", "width", @@ -1123,6 +1245,21 @@ static void parse_crop(char *optarg, unsigned int &set_crop, v4l2_rect &vcrop) } } +static enum v4l2_field parse_field(const char *s) +{ + if (!strcmp(s, "any")) return V4L2_FIELD_ANY; + if (!strcmp(s, "none")) return V4L2_FIELD_NONE; + if (!strcmp(s, "top")) return V4L2_FIELD_TOP; + if (!strcmp(s, "bottom")) return V4L2_FIELD_BOTTOM; + if (!strcmp(s, "interlaced")) return V4L2_FIELD_INTERLACED; + if (!strcmp(s, "seq_tb")) return V4L2_FIELD_SEQ_TB; + if (!strcmp(s, "seq_bt")) return V4L2_FIELD_SEQ_BT; + if (!strcmp(s, "alternate")) return V4L2_FIELD_ALTERNATE; + if (!strcmp(s, "interlaced_tb")) return V4L2_FIELD_INTERLACED_TB; + if (!strcmp(s, "interlaced_bt")) return V4L2_FIELD_INTERLACED_BT; + return V4L2_FIELD_ANY; +} + int main(int argc, char **argv) { char *value, *subs; @@ -1138,19 +1275,21 @@ int main(int argc, char **argv) unsigned int set_crop_overlay = 0; unsigned int set_crop_out_overlay = 0; unsigned int set_fbuf = 0; + unsigned int set_overlay_fmt = 0; unsigned int set_overlay_fmt_out = 0; int mode = V4L2_TUNER_MODE_STEREO; /* set audio mode */ /* command args */ int ch; - char *device = strdup("/dev/video0"); /* -d device */ + const char *device = "/dev/video0"; /* -d device */ struct v4l2_format vfmt; /* set_format/get_format for video */ struct v4l2_format vfmt_out; /* set_format/get_format video output */ struct v4l2_format vbi_fmt; /* set_format/get_format for sliced VBI */ struct v4l2_format vbi_fmt_out; /* set_format/get_format for sliced VBI output */ struct v4l2_format raw_fmt; /* set_format/get_format for VBI */ struct v4l2_format raw_fmt_out; /* set_format/get_format for VBI output */ + struct v4l2_format overlay_fmt; /* set_format/get_format video overlay */ struct v4l2_format overlay_fmt_out; /* set_format/get_format video overlay output */ struct v4l2_tuner tuner; /* set_tuner/get_tuner */ struct v4l2_capability vcap; /* list_cap */ @@ -1170,14 +1309,18 @@ int main(int argc, char **argv) struct v4l2_frequency vf; /* get_freq/set_freq */ struct v4l2_standard vs; /* list_std */ int overlay; /* overlay */ + unsigned int *set_overlay_fmt_ptr; + struct v4l2_format *overlay_fmt_ptr; char short_options[26 * 2 * 2 + 1]; int idx = 0; + int ret; memset(&vfmt, 0, sizeof(vfmt)); memset(&vbi_fmt, 0, sizeof(vbi_fmt)); memset(&raw_fmt, 0, sizeof(raw_fmt)); memset(&vfmt_out, 0, sizeof(vfmt_out)); memset(&vbi_fmt_out, 0, sizeof(vbi_fmt_out)); + memset(&overlay_fmt, 0, sizeof(overlay_fmt)); memset(&overlay_fmt_out, 0, sizeof(overlay_fmt_out)); memset(&raw_fmt_out, 0, sizeof(raw_fmt_out)); memset(&tuner, 0, sizeof(tuner)); @@ -1220,19 +1363,23 @@ int main(int argc, char **argv) usage(); return 0; case OptSetDevice: - device = strdup(optarg); + device = optarg; if (device[0] >= '0' && device[0] <= '9' && device[1] == 0) { + static char newdev[20]; char dev = device[0]; - sprintf(device, "/dev/video%c", dev); + sprintf(newdev, "/dev/video%c", dev); + device = newdev; } break; case OptSetVideoFormat: + case OptTryVideoFormat: subs = optarg; while (*subs != '\0') { - static char *const subopts[] = { + static const char *const subopts[] = { "width", "height", + "pixelformat", NULL }; @@ -1245,13 +1392,23 @@ int main(int argc, char **argv) vfmt.fmt.pix.height = strtol(value, 0L, 0); set_fmts |= FmtHeight; break; + case 2: + if (strlen(value) == 4) + vfmt.fmt.pix.pixelformat = + v4l2_fourcc(value[0], value[1], + value[2], value[3]); + else + vfmt.fmt.pix.pixelformat = strtol(value, 0L, 0); + set_fmts |= FmtPixelFormat; + break; } } break; case OptSetVideoOutFormat: + case OptTryVideoOutFormat: subs = optarg; while (*subs != '\0') { - static char *const subopts[] = { + static const char *const subopts[] = { "width", "height", NULL @@ -1269,23 +1426,63 @@ int main(int argc, char **argv) } } break; + case OptSetOverlayFormat: + case OptTryOverlayFormat: case OptSetOutputOverlayFormat: + case OptTryOutputOverlayFormat: + switch (ch) { + case OptSetOverlayFormat: + case OptTryOverlayFormat: + set_overlay_fmt_ptr = &set_overlay_fmt; + overlay_fmt_ptr = &overlay_fmt; + break; + case OptSetOutputOverlayFormat: + case OptTryOutputOverlayFormat: + set_overlay_fmt_ptr = &set_overlay_fmt_out; + overlay_fmt_ptr = &overlay_fmt_out; + break; + } subs = optarg; while (*subs != '\0') { - static char *const subopts[] = { + static const char *const subopts[] = { "chromakey", "global_alpha", + "left", + "top", + "width", + "height", + "field", NULL }; switch (parse_subopt(&subs, subopts, &value)) { case 0: - overlay_fmt_out.fmt.win.chromakey = strtol(value, 0L, 0); - set_overlay_fmt_out |= FmtChromaKey; + overlay_fmt_ptr->fmt.win.chromakey = strtol(value, 0L, 0); + *set_overlay_fmt_ptr |= FmtChromaKey; break; case 1: - overlay_fmt_out.fmt.win.global_alpha = strtol(value, 0L, 0); - set_overlay_fmt_out |= FmtGlobalAlpha; + overlay_fmt_ptr->fmt.win.global_alpha = strtol(value, 0L, 0); + *set_overlay_fmt_ptr |= FmtGlobalAlpha; + break; + case 2: + overlay_fmt_ptr->fmt.win.w.left = strtol(value, 0L, 0); + *set_overlay_fmt_ptr |= FmtLeft; + break; + case 3: + overlay_fmt_ptr->fmt.win.w.top = strtol(value, 0L, 0); + *set_overlay_fmt_ptr |= FmtTop; + break; + case 4: + overlay_fmt_ptr->fmt.win.w.width = strtol(value, 0L, 0); + *set_overlay_fmt_ptr |= FmtWidth; + break; + case 5: + overlay_fmt_ptr->fmt.win.w.height = strtol(value, 0L, 0); + *set_overlay_fmt_ptr |= FmtHeight; + break; + case 6: + overlay_fmt_ptr->fmt.win.field = parse_field(value); + *set_overlay_fmt_ptr |= FmtField; break; } } @@ -1293,7 +1490,7 @@ int main(int argc, char **argv) case OptSetFBuf: subs = optarg; while (*subs != '\0') { - static char *const subopts[] = { + static const char *const subopts[] = { "chromakey", "global_alpha", "local_alpha", @@ -1419,16 +1616,18 @@ int main(int argc, char **argv) break; case OptSetSlicedVbiFormat: case OptSetSlicedVbiOutFormat: + case OptTrySlicedVbiFormat: + case OptTrySlicedVbiOutFormat: { bool foundOff = false; v4l2_format *fmt = &vbi_fmt; - if (ch == OptSetSlicedVbiOutFormat) + if (ch == OptSetSlicedVbiOutFormat || ch == OptTrySlicedVbiOutFormat) fmt = &vbi_fmt_out; fmt->fmt.sliced.service_set = 0; subs = optarg; while (*subs != '\0') { - static char *const subopts[] = { + static const char *const subopts[] = { "off", "teletext", "cc", @@ -1466,6 +1665,9 @@ int main(int argc, char **argv) } break; } + case OptListDevices: + list_devices(); + break; case ':': fprintf(stderr, "Option `%s' requires a value\n", argv[optind]); @@ -1492,8 +1694,8 @@ int main(int argc, char **argv) strerror(errno)); exit(1); } - free(device); + verbose = options[OptVerbose]; doioctl(fd, VIDIOC_QUERYCAP, &vcap, "VIDIOC_QUERYCAP"); capabilities = vcap.capabilities; find_controls(fd); @@ -1531,6 +1733,7 @@ int main(int argc, char **argv) options[OptGetFBuf] = 1; options[OptGetCropCap] = 1; options[OptGetOutputCropCap] = 1; + options[OptSilent] = 1; } /* Information Opts */ @@ -1613,7 +1816,7 @@ int main(int argc, char **argv) } } - if (options[OptSetVideoFormat]) { + if (options[OptSetVideoFormat] || options[OptTryVideoFormat]) { struct v4l2_format in_vfmt; in_vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; @@ -1622,11 +1825,29 @@ int main(int argc, char **argv) in_vfmt.fmt.pix.width = vfmt.fmt.pix.width; if (set_fmts & FmtHeight) in_vfmt.fmt.pix.height = vfmt.fmt.pix.height; - doioctl(fd, VIDIOC_S_FMT, &in_vfmt, "VIDIOC_S_FMT"); + if (set_fmts & FmtPixelFormat) { + in_vfmt.fmt.pix.pixelformat = vfmt.fmt.pix.pixelformat; + if (in_vfmt.fmt.pix.pixelformat < 256) { + struct v4l2_fmtdesc fmt; + + fmt.index = in_vfmt.fmt.pix.pixelformat; + fmt.type = in_vfmt.type; + if (doioctl(fd, VIDIOC_ENUM_FMT, &fmt, "VIDIOC_ENUM_FMT")) + goto set_vid_fmt_error; + in_vfmt.fmt.pix.pixelformat = fmt.pixelformat; + } + } + if (options[OptSetVideoFormat]) + ret = doioctl(fd, VIDIOC_S_FMT, &in_vfmt, "VIDIOC_S_FMT"); + else + ret = doioctl(fd, VIDIOC_TRY_FMT, &in_vfmt, "VIDIOC_TRY_FMT"); + if (ret == 0 && verbose) + printfmt(in_vfmt); } } +set_vid_fmt_error: - if (options[OptSetVideoOutFormat]) { + if (options[OptSetVideoOutFormat] || options[OptTryVideoOutFormat]) { struct v4l2_format in_vfmt; in_vfmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; @@ -1635,35 +1856,65 @@ int main(int argc, char **argv) in_vfmt.fmt.pix.width = vfmt_out.fmt.pix.width; if (set_fmts_out & FmtHeight) in_vfmt.fmt.pix.height = vfmt_out.fmt.pix.height; - doioctl(fd, VIDIOC_S_FMT, &in_vfmt, "VIDIOC_S_FMT"); + + if (options[OptSetVideoOutFormat]) + ret = doioctl(fd, VIDIOC_S_FMT, &in_vfmt, "VIDIOC_S_FMT"); + else + ret = doioctl(fd, VIDIOC_TRY_FMT, &in_vfmt, "VIDIOC_TRY_FMT"); + if (ret == 0 && verbose) + printfmt(in_vfmt); } } - if (options[OptSetSlicedVbiFormat]) { - if (vbi_fmt.fmt.sliced.service_set == 0) { - // switch to raw mode - vbi_fmt.type = V4L2_BUF_TYPE_VBI_CAPTURE; - if (doioctl(fd, VIDIOC_G_FMT, &vbi_fmt, "VIDIOC_G_FMT") == 0) - doioctl(fd, VIDIOC_S_FMT, &vbi_fmt, "VIDIOC_S_FMT"); - } else { - vbi_fmt.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE; - doioctl(fd, VIDIOC_S_FMT, &vbi_fmt, "VIDIOC_S_FMT"); - } + if (options[OptSetSlicedVbiFormat] || options[OptTrySlicedVbiFormat]) { + vbi_fmt.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE; + if (options[OptSetSlicedVbiFormat]) + ret = doioctl(fd, VIDIOC_S_FMT, &vbi_fmt, "VIDIOC_S_FMT"); + else + ret = doioctl(fd, VIDIOC_TRY_FMT, &vbi_fmt, "VIDIOC_TRY_FMT"); + if (ret == 0 && verbose) + printfmt(vbi_fmt); } - if (options[OptSetSlicedVbiOutFormat]) { - if (vbi_fmt_out.fmt.sliced.service_set == 0) { - // switch to raw mode - vbi_fmt_out.type = V4L2_BUF_TYPE_VBI_OUTPUT; - if (doioctl(fd, VIDIOC_G_FMT, &vbi_fmt_out, "VIDIOC_G_FMT") == 0) - doioctl(fd, VIDIOC_S_FMT, &vbi_fmt_out, "VIDIOC_S_FMT"); - } else { - vbi_fmt_out.type = V4L2_BUF_TYPE_SLICED_VBI_OUTPUT; - doioctl(fd, VIDIOC_S_FMT, &vbi_fmt_out, "VIDIOC_S_FMT"); + if (options[OptSetSlicedVbiOutFormat] || options[OptTrySlicedVbiOutFormat]) { + vbi_fmt_out.type = V4L2_BUF_TYPE_SLICED_VBI_OUTPUT; + if (options[OptSetSlicedVbiOutFormat]) + ret = doioctl(fd, VIDIOC_S_FMT, &vbi_fmt_out, "VIDIOC_S_FMT"); + else + ret = doioctl(fd, VIDIOC_TRY_FMT, &vbi_fmt_out, "VIDIOC_TRY_FMT"); + if (ret == 0 && verbose) + printfmt(vbi_fmt_out); + } + + if (options[OptSetOverlayFormat] || options[OptTryOverlayFormat]) { + struct v4l2_format fmt; + + fmt.type = V4L2_BUF_TYPE_VIDEO_OVERLAY; + if (doioctl(fd, VIDIOC_G_FMT, &fmt, "VIDIOC_G_FMT") == 0) { + if (set_overlay_fmt & FmtChromaKey) + fmt.fmt.win.chromakey = overlay_fmt.fmt.win.chromakey; + if (set_overlay_fmt & FmtGlobalAlpha) + fmt.fmt.win.global_alpha = overlay_fmt.fmt.win.global_alpha; + if (set_overlay_fmt & FmtLeft) + fmt.fmt.win.w.left = overlay_fmt.fmt.win.w.left; + if (set_overlay_fmt & FmtTop) + fmt.fmt.win.w.top = overlay_fmt.fmt.win.w.top; + if (set_overlay_fmt & FmtWidth) + fmt.fmt.win.w.width = overlay_fmt.fmt.win.w.width; + if (set_overlay_fmt & FmtHeight) + fmt.fmt.win.w.height = overlay_fmt.fmt.win.w.height; + if (set_overlay_fmt & FmtField) + fmt.fmt.win.field = overlay_fmt.fmt.win.field; + if (options[OptSetOverlayFormat]) + ret = doioctl(fd, VIDIOC_S_FMT, &fmt, "VIDIOC_S_FMT"); + else + ret = doioctl(fd, VIDIOC_TRY_FMT, &fmt, "VIDIOC_TRY_FMT"); + if (ret == 0 && verbose) + printfmt(fmt); } } - if (options[OptSetOutputOverlayFormat]) { + if (options[OptSetOutputOverlayFormat] || options[OptTryOutputOverlayFormat]) { struct v4l2_format fmt; fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY; @@ -1672,7 +1923,22 @@ int main(int argc, char **argv) fmt.fmt.win.chromakey = overlay_fmt_out.fmt.win.chromakey; if (set_overlay_fmt_out & FmtGlobalAlpha) fmt.fmt.win.global_alpha = overlay_fmt_out.fmt.win.global_alpha; - doioctl(fd, VIDIOC_S_FMT, &fmt, "VIDIOC_S_FMT"); + if (set_overlay_fmt_out & FmtLeft) + fmt.fmt.win.w.left = overlay_fmt_out.fmt.win.w.left; + if (set_overlay_fmt_out & FmtTop) + fmt.fmt.win.w.top = overlay_fmt_out.fmt.win.w.top; + if (set_overlay_fmt_out & FmtWidth) + fmt.fmt.win.w.width = overlay_fmt_out.fmt.win.w.width; + if (set_overlay_fmt_out & FmtHeight) + fmt.fmt.win.w.height = overlay_fmt_out.fmt.win.w.height; + if (set_overlay_fmt_out & FmtField) + fmt.fmt.win.field = overlay_fmt_out.fmt.win.field; + if (options[OptSetOutputOverlayFormat]) + ret = doioctl(fd, VIDIOC_S_FMT, &fmt, "VIDIOC_S_FMT"); + else + ret = doioctl(fd, VIDIOC_TRY_FMT, &fmt, "VIDIOC_TRY_FMT"); + if (ret == 0 && verbose) + printfmt(fmt); } } @@ -1992,6 +2258,7 @@ int main(int argc, char **argv) memset(&vt, 0, sizeof(struct v4l2_tuner)); if (doioctl(fd, VIDIOC_G_TUNER, &vt, "VIDIOC_G_TUNER") == 0) { printf("Tuner:\n"); + printf("\tName : %s\n", vt.name); printf("\tCapabilities : %s\n", tcap2s(vt.capability).c_str()); if (vt.capability & V4L2_TUNER_CAP_LOW) printf("\tFrequency range : %.1f MHz - %.1f MHz\n", @@ -1999,7 +2266,7 @@ int main(int argc, char **argv) else printf("\tFrequency range : %.1f MHz - %.1f MHz\n", vt.rangelow / 16.0, vt.rangehigh / 16.0); - printf("\tSignal strength : %d%%\n", (int)(vt.signal / 655.35)); + printf("\tSignal strength/AFC : %d%%/%d\n", (int)(vt.signal / 655.35), vt.afc); printf("\tCurrent audio mode : %s\n", audmode2s(vt.audmode)); printf("\tAvailable subchannels: %s\n", rxsubchans2s(vt.rxsubchans).c_str()); @@ -2113,7 +2380,7 @@ int main(int argc, char **argv) while (ioctl(fd, VIDIOC_ENUMSTD, &vs) >= 0) { if (vs.index) printf("\n"); - printf("\tindex : %d\n", vs.index); + printf("\tIndex : %d\n", vs.index); printf("\tID : 0x%016llX\n", (unsigned long long)vs.id); printf("\tName : %s\n", vs.name); printf("\tFrame period: %d/%d\n", diff --git a/v4l2-apps/util/bttv-dbg.h b/v4l2-apps/util/v4l2-dbg-bttv.h index 02f829773..cf6274284 100644 --- a/v4l2-apps/util/bttv-dbg.h +++ b/v4l2-apps/util/v4l2-dbg-bttv.h @@ -14,7 +14,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "v4l-board-dbg.h" +#include "v4l2-dbg.h" #define BTTV_IDENT "bttv" diff --git a/v4l2-apps/util/em28xx-dbg.h b/v4l2-apps/util/v4l2-dbg-em28xx.h index d2edc60bc..c5117c6e7 100644 --- a/v4l2-apps/util/em28xx-dbg.h +++ b/v4l2-apps/util/v4l2-dbg-em28xx.h @@ -14,7 +14,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "v4l-board-dbg.h" +#include "v4l2-dbg.h" #define EM28XX_IDENT "em28xx" @@ -81,4 +81,4 @@ static struct board_regs em28xx_regs[] = { {0x02, EM28XX_PREFIX "MASTER_AC97", 1}, {0x10, EM28XX_PREFIX "LINE_IN_AC97", 1}, {0x14, EM28XX_PREFIX "VIDEO_AC97", 1}, -};
\ No newline at end of file +}; diff --git a/v4l2-apps/util/saa7134-dbg.h b/v4l2-apps/util/v4l2-dbg-saa7134.h index aee29da76..70fd4e068 100644 --- a/v4l2-apps/util/saa7134-dbg.h +++ b/v4l2-apps/util/v4l2-dbg-saa7134.h @@ -14,7 +14,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "v4l-board-dbg.h" +#include "v4l2-dbg.h" #define SAA7134_IDENT "saa7134" diff --git a/v4l2-apps/util/v4l2-dbg.cpp b/v4l2-apps/util/v4l2-dbg.cpp index 8323f2120..e0d6153fe 100644 --- a/v4l2-apps/util/v4l2-dbg.cpp +++ b/v4l2-apps/util/v4l2-dbg.cpp @@ -42,6 +42,48 @@ #include <map> #include <string> +#include "v4l2-dbg-bttv.h" +#include "v4l2-dbg-saa7134.h" +#include "v4l2-dbg-em28xx.h" + +#define ARRAY_SIZE(arr) ((int)(sizeof(arr) / sizeof((arr)[0]))) + +struct board_list { + const char *name; + int prefix; /* Register prefix size */ + const struct board_regs *regs; + int regs_size; + const struct board_regs *alt_regs; + int alt_regs_size; +}; + +static const struct board_list boards[] = { + { /* From bttv-dbg.h */ + BTTV_IDENT, + sizeof(BTTV_PREFIX) - 1, + bt8xx_regs, + ARRAY_SIZE(bt8xx_regs), + bt8xx_regs_other, + ARRAY_SIZE(bt8xx_regs_other), + }, + { /* From saa7134-dbg.h */ + SAA7134_IDENT, + sizeof(SAA7134_PREFIX) - 1, + saa7134_regs, + ARRAY_SIZE(saa7134_regs), + NULL, + 0, + }, + { /* From em28xx-dbg.h */ + EM28XX_IDENT, + sizeof(EM28XX_PREFIX) - 1, + em28xx_regs, + ARRAY_SIZE(em28xx_regs), + NULL, + 0, + }, +}; + struct driverid { const char *name; unsigned id; @@ -64,19 +106,21 @@ extern struct chipid chipids[]; In general the lower case is used to set something and the upper case is used to retrieve a setting. */ enum Option { - OptListRegisters = 'R', - OptSetRegister = 'r', - OptSetSlicedVbiFormat = 'b', + OptListRegisters = 'l', + OptGetRegister = 'g', + OptSetRegister = 's', OptSetDevice = 'd', OptGetDriverInfo = 'D', - OptScanChipIdents = 'C', - OptGetChipIdent = 'c', + OptChip = 'c', + OptScanChipIdents = 'S', + OptGetChipIdent = 'i', OptSetStride = 'w', OptHelp = 'h', OptLogStatus = 128, OptVerbose, OptListDriverIDs, + OptListSymbols, OptLast = 256 }; @@ -87,56 +131,56 @@ static unsigned capabilities; static struct option long_options[] = { {"device", required_argument, 0, OptSetDevice}, {"help", no_argument, 0, OptHelp}, - {"list-registers", required_argument, 0, OptListRegisters}, + {"list-registers", optional_argument, 0, OptListRegisters}, + {"get-register", required_argument, 0, OptGetRegister}, {"set-register", required_argument, 0, OptSetRegister}, + {"chip", required_argument, 0, OptChip}, {"scan-chip-idents", no_argument, 0, OptScanChipIdents}, {"get-chip-ident", required_argument, 0, OptGetChipIdent}, {"info", no_argument, 0, OptGetDriverInfo}, {"verbose", no_argument, 0, OptVerbose}, {"log-status", no_argument, 0, OptLogStatus}, {"list-driverids", no_argument, 0, OptListDriverIDs}, + {"list-symbols", no_argument, 0, OptListSymbols}, {"wide", required_argument, 0, OptSetStride}, {0, 0, 0, 0} }; static void usage(void) { - printf("Usage:\n"); - printf(" -D, --info show driver info [VIDIOC_QUERYCAP]\n"); - printf(" -d, --device=<dev> use device <dev> instead of /dev/video0\n"); - printf(" if <dev> is a single digit, then /dev/video<dev> is used\n"); - printf(" -h, --help display this help message\n"); - printf(" --verbose turn on verbose ioctl error reporting.\n"); - printf(" -R, --list-registers=type=<host/i2cdrv/i2caddr>,chip=<chip>[,min=<addr>,max=<addr>] \n"); - printf(" dump registers from <min> to <max> [VIDIOC_DBG_G_REGISTER]\n"); - printf(" -r, --set-register=type=<host/i2cdrv/i2caddr>,chip=<chip>,reg=<addr>,val=<val>\n"); - printf(" set the register [VIDIOC_DBG_S_REGISTER]\n"); - printf(" -C, --scan-chip-idents\n"); - printf(" Scan the available host and i2c chips [VIDIOC_G_CHIP_IDENT]\n"); - printf(" -c, --get-chip-ident=type=<host/i2cdrv/i2caddr>,chip=<chip>\n"); - printf(" Get the chip identifier [VIDIOC_G_CHIP_IDENT]\n"); - printf(" -w, --wide=<reg length>\n"); - printf(" Sets step between two registers\n"); - printf(" --log-status log the board status in the kernel log [VIDIOC_LOG_STATUS]\n"); - printf(" --list-driverids list the known I2C driver IDs for use with the i2cdrv type\n"); - printf("\n"); - printf(" if type == host, then <chip> is the host's chip ID (default 0)\n"); - printf(" if type == i2cdrv (default), then <chip> is the I2C driver name or ID\n"); - printf(" if type == i2caddr, then <chip> is the 7-bit I2C address\n"); + printf("Usage: v4l2-dbg [options] [values]\n" + " -D, --info Show driver info [VIDIOC_QUERYCAP]\n" + " -d, --device=<dev> Use device <dev> instead of /dev/video0\n" + " If <dev> is a single digit, then /dev/video<dev> is used\n" + " -h, --help Display this help message\n" + " --verbose Turn on verbose ioctl error reporting\n" + " -c, --chip=<chip> The chip identifier to use with other commands\n" + " It can be one of:\n" + " I2C driver ID (see --list-driverids)\n" + " I2C 7-bit address\n" + " host<num>: host chip number <num>\n" + " host (default): same as host0\n" + " -l, --list-registers[=min=<addr>[,max=<addr>]]\n" + " Dump registers from <min> to <max> [VIDIOC_DBG_G_REGISTER]\n" + " -g, --get-register=<addr>\n" + " Get the specified register [VIDIOC_DBG_G_REGISTER]\n" + " -s, --set-register=<addr>\n" + " Set the register with the commandline arguments\n" + " The register will autoincrement [VIDIOC_DBG_S_REGISTER]\n" + " -S, --scan-chip-idents\n" + " Scan the available host and i2c chips [VIDIOC_G_CHIP_IDENT]\n" + " -i, --get-chip-ident\n" + " Get the chip identifier [VIDIOC_G_CHIP_IDENT]\n" + " -w, --wide=<reg length>\n" + " Sets step between two registers\n" + " --list-symbols List the symbolic register names you can use, if any\n" + " --log-status Log the board status in the kernel log [VIDIOC_LOG_STATUS]\n" + " --list-driverids List the known I2C driver IDs for use with the i2cdrv type\n"); exit(0); } -static unsigned parse_type(const std::string &s) -{ - if (s == "host") return V4L2_CHIP_MATCH_HOST; - if (s == "i2caddr") return V4L2_CHIP_MATCH_I2C_ADDR; - return V4L2_CHIP_MATCH_I2C_DRIVER; -} - -static unsigned parse_chip(int type, const std::string &s) +static unsigned parse_chip(const std::string &s) { - if (type == V4L2_CHIP_MATCH_HOST || type == V4L2_CHIP_MATCH_I2C_ADDR || isdigit(s[0])) - return strtoul(s.c_str(), 0, 0); for (int i = 0; driverids[i].name; i++) if (!strcasecmp(s.c_str(), driverids[i].name)) return driverids[i].id; @@ -230,6 +274,56 @@ static void print_chip(struct v4l2_chip_ident *chip) printf("%-10d revision 0x%08x\n", chip->ident, chip->revision); } +static unsigned long long parse_reg(const struct board_list *curr_bd, const std::string ®) +{ + if (curr_bd) { + for (int i = 0; i < curr_bd->regs_size; i++) { + if (!strcasecmp(reg.c_str(), curr_bd->regs[i].name) || + !strcasecmp(reg.c_str(), curr_bd->regs[i].name + curr_bd->prefix)) { + return curr_bd->regs[i].reg; + } + } + for (int i = 0; i < curr_bd->alt_regs_size; i++) { + if (!strcasecmp(reg.c_str(), curr_bd->alt_regs[i].name) || + !strcasecmp(reg.c_str(), curr_bd->alt_regs[i].name + curr_bd->prefix)) { + return curr_bd->alt_regs[i].reg; + } + } + } + return strtoull(reg.c_str(), NULL, 0); +} + +static const char *binary(unsigned long long val) +{ + static char bin[80]; + char *p = bin; + int i, j; + int bits = 64; + + if ((val & 0xffffffff00000000LL) == 0) { + if ((val & 0xffff0000) == 0) { + if ((val & 0xff00) == 0) + bits = 8; + else + bits= 16; + } + else + bits = 32; + } + + for (i = bits - 1; i >= 0; i -= 8) { + for (j = i; j >= i - 7; j--) { + if (val & (1LL << j)) + *p++ = '1'; + else + *p++ = '0'; + } + *p++ = ' '; + } + p[-1] = 0; + return bin; +} + static int doioctl(int fd, int request, void *parm, const char *name) { int retVal; @@ -245,9 +339,9 @@ static int doioctl(int fd, int request, void *parm, const char *name) return retVal; } -static int parse_subopt(char **subs, char * const *subopts, char **value) +static int parse_subopt(char **subs, const char * const *subopts, char **value) { - int opt = getsubopt(subs, subopts, value); + int opt = getsubopt(subs, (char * const *)subopts, value); if (opt == -1) { fprintf(stderr, "Invalid suboptions specified\n"); @@ -272,14 +366,20 @@ int main(int argc, char **argv) /* command args */ int ch; - char *device = strdup("/dev/video0"); /* -d device */ + const char *device = "/dev/video0"; /* -d device */ struct v4l2_capability vcap; /* list_cap */ struct v4l2_register set_reg; struct v4l2_register get_reg; struct v4l2_chip_ident chip_id; + const struct board_list *curr_bd = NULL; char short_options[26 * 2 * 2 + 1]; int idx = 0; + std::string reg_min_arg, reg_max_arg; + std::string reg_set_arg; unsigned long long reg_min = 0, reg_max = 0; + std::vector<std::string> get_regs; + int match_type = V4L2_CHIP_MATCH_HOST; + int match_chip = 0; memset(&set_reg, 0, sizeof(set_reg)); memset(&get_reg, 0, sizeof(get_reg)); @@ -310,51 +410,55 @@ int main(int argc, char **argv) case OptHelp: usage(); return 0; + case OptSetDevice: - device = strdup(optarg); + device = optarg; if (device[0] >= '0' && device[0] <= '9' && device[1] == 0) { + static char newdev[20]; char dev = device[0]; - sprintf(device, "/dev/video%c", dev); + sprintf(newdev, "/dev/video%c", dev); + device = newdev; } break; - case OptSetRegister: - subs = optarg; - set_reg.match_type = V4L2_CHIP_MATCH_I2C_DRIVER; - while (*subs != '\0') { - static char *const subopts[] = { - "type", - "chip", - "reg", - "val", - NULL - }; - switch (parse_subopt(&subs, subopts, &value)) { - case 0: - set_reg.match_type = parse_type(value); - break; - case 1: - set_reg.match_chip = parse_chip(set_reg.match_type, value); - break; - case 2: - set_reg.reg = strtoull(value, 0L, 0); - break; - case 3: - set_reg.val = strtoull(value, 0L, 0); - break; - } + case OptChip: + if (isdigit(optarg[0])) { + match_type = V4L2_CHIP_MATCH_I2C_ADDR; + match_chip = strtoul(optarg, NULL, 0); + break; + } + if (!memcmp(optarg, "host", 4)) { + match_type = V4L2_CHIP_MATCH_HOST; + match_chip = strtoul(optarg + 4, NULL, 0); + break; } + match_type = V4L2_CHIP_MATCH_I2C_DRIVER; + match_chip = parse_chip(optarg); + if (!match_chip) { + fprintf(stderr, "unknown driver ID %s\n", optarg); + exit(-1); + } + break; + + case OptSetRegister: + reg_set_arg = optarg; + break; + + case OptGetRegister: + get_regs.push_back(optarg); + break; + case OptSetStride: forcedstride = strtoull(optarg, 0L, 0); break; + case OptListRegisters: subs = optarg; - get_reg.match_type = V4L2_CHIP_MATCH_I2C_DRIVER; + if (subs == NULL) + break; while (*subs != '\0') { - static char *const subopts[] = { - "type", - "chip", + static const char * const subopts[] = { "min", "max", NULL @@ -362,47 +466,27 @@ int main(int argc, char **argv) switch (parse_subopt(&subs, subopts, &value)) { case 0: - get_reg.match_type = parse_type(value); + reg_min_arg = value; + //if (reg_max == 0) + // reg_max = reg_min + 0xff; break; case 1: - get_reg.match_chip = parse_chip(get_reg.match_type, value); - break; - case 2: - reg_min = strtoull(value, 0L, 0); - if (reg_max == 0) - reg_max = reg_min + 0xff; - break; - case 3: - reg_max = strtoull(value, 0L, 0); + reg_max_arg = value; break; } } break; - case OptGetChipIdent: - subs = optarg; - set_reg.match_type = V4L2_CHIP_MATCH_I2C_DRIVER; - while (*subs != '\0') { - static char *const subopts[] = { - "type", - "chip", - NULL - }; - switch (parse_subopt(&subs, subopts, &value)) { - case 0: - chip_id.match_type = parse_type(value); - break; - case 1: - chip_id.match_chip = parse_chip(chip_id.match_type, value); - break; - } - } + case OptGetChipIdent: + case OptListSymbols: break; + case ':': fprintf(stderr, "Option `%s' requires a value\n", argv[optind]); usage(); return 1; + case '?': fprintf(stderr, "Unknown argument `%s'\n", argv[optind]); @@ -410,21 +494,12 @@ int main(int argc, char **argv) return 1; } } - if (optind < argc) { - printf("unknown arguments: "); - while (optind < argc) - printf("%s ", argv[optind++]); - printf("\n"); - usage(); - return 1; - } if ((fd = open(device, O_RDWR)) < 0) { fprintf(stderr, "Failed to open %s: %s\n", device, strerror(errno)); exit(1); } - free(device); doioctl(fd, VIDIOC_QUERYCAP, &vcap, "VIDIOC_QUERYCAP"); capabilities = vcap.capabilities; @@ -441,15 +516,33 @@ int main(int argc, char **argv) printf("%s", cap2s(vcap.capabilities).c_str()); } + for (int board = ARRAY_SIZE(boards) - 1; board >= 0; board--) { + if (!strcasecmp((char *)vcap.driver, boards[board].name)) { + curr_bd = &boards[board]; + break; + } + } + /* Set options */ if (options[OptSetRegister]) { - if (doioctl(fd, VIDIOC_DBG_S_REGISTER, &set_reg, - "VIDIOC_DBG_S_REGISTER") == 0) - printf("register 0x%llx set to 0x%llx\n", set_reg.reg, set_reg.val); + set_reg.match_type = match_type; + set_reg.match_chip = match_chip; + if (optind >= argc) + usage(); + set_reg.reg = parse_reg(curr_bd, reg_set_arg); + while (optind < argc) { + set_reg.val = strtoull(argv[optind++], NULL, 0); + if (doioctl(fd, VIDIOC_DBG_S_REGISTER, &set_reg, + "VIDIOC_DBG_S_REGISTER") == 0) + printf("register 0x%llx set to 0x%llx\n", set_reg.reg, set_reg.val); + set_reg.reg++; + } } if (options[OptGetChipIdent]) { + chip_id.match_type = match_type; + chip_id.match_chip = match_chip; if (doioctl(fd, VIDIOC_G_CHIP_IDENT, &chip_id, "VIDIOC_G_CHIP_IDENT") == 0) print_chip(&chip_id); } @@ -461,7 +554,7 @@ int main(int argc, char **argv) chip_id.match_chip = 0; while (doioctl(fd, VIDIOC_G_CHIP_IDENT, &chip_id, "VIDIOC_G_CHIP_IDENT") == 0 && chip_id.ident) { - printf("host 0x%x: ", chip_id.match_chip); + printf("host%d: ", chip_id.match_chip); print_chip(&chip_id); chip_id.match_chip++; } @@ -476,8 +569,30 @@ int main(int argc, char **argv) } } + if (options[OptGetRegister]) { + int stride = 1; + + get_reg.match_type = match_type; + get_reg.match_chip = match_chip; + printf("ioctl: VIDIOC_DBG_G_REGISTER\n"); + + for (std::vector<std::string>::iterator iter = get_regs.begin(); + iter != get_regs.end(); ++iter) { + get_reg.reg = parse_reg(curr_bd, *iter); + if (ioctl(fd, VIDIOC_DBG_G_REGISTER, &get_reg) < 0) + fprintf(stderr, "ioctl: VIDIOC_DBG_G_REGISTER " + "failed for 0x%llx\n", get_reg.reg); + else + printf("%llx = %llxh = %lldd = %sb\n", get_reg.reg, + get_reg.val, get_reg.val, binary(get_reg.val)); + } + } + if (options[OptListRegisters]) { int stride = 1; + + get_reg.match_type = match_type; + get_reg.match_chip = match_chip; if (forcedstride) { stride = forcedstride; } else { @@ -486,7 +601,12 @@ int main(int argc, char **argv) } printf("ioctl: VIDIOC_DBG_G_REGISTER\n"); - if (reg_max != 0) { + if (!reg_min_arg.empty()) { + reg_min = parse_reg(curr_bd, reg_min_arg); + if (reg_max_arg.empty()) + reg_max = reg_min + 0xff; + else + reg_max = parse_reg(curr_bd, reg_max_arg); /* Explicit memory range: just do it */ print_regs(fd, &get_reg, reg_min, reg_max, stride); goto list_done; @@ -579,6 +699,19 @@ list_done: printf("%s\n", driverids[i].name); } + if (options[OptListSymbols]) { + if (curr_bd == NULL) { + printf("No symbols found for driver %s\n", vcap.driver); + } + else { + printf("Symbols for driver %s:\n", vcap.driver); + for (int i = 0; i < curr_bd->regs_size; i++) + printf("0x%08x: %s\n", curr_bd->regs[i], curr_bd->regs[i].name); + for (int i = 0; i < curr_bd->alt_regs_size; i++) + printf("0x%08x: %s\n", curr_bd->alt_regs[i], curr_bd->alt_regs[i].name); + } + } + close(fd); exit(0); } diff --git a/v4l2-apps/util/v4l-board-dbg.h b/v4l2-apps/util/v4l2-dbg.h index 5d040812d..06ceb0ea2 100644 --- a/v4l2-apps/util/v4l-board-dbg.h +++ b/v4l2-apps/util/v4l2-dbg.h @@ -14,11 +14,13 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef _V4L_BOARD_DBG -#define _V4L_BOARD_DBG +#ifndef _V4L2_DBG_H_ +#define _V4L2_DBG_H_ + struct board_regs { unsigned int reg; - char *name; + const char *name; int size; }; -#endif
\ No newline at end of file + +#endif diff --git a/v4l2-apps/util/v4l_rec.pl b/v4l2-apps/util/v4l_rec.pl index b533af097..941c03634 100755 --- a/v4l2-apps/util/v4l_rec.pl +++ b/v4l2-apps/util/v4l_rec.pl @@ -16,7 +16,7 @@ $std=shift or $std='PAL-M'; $dev=shift or $dev="/dev/video1"; ############################################## -# Those stuff bellow are currently "hardcoded" +# Those stuff below are currently "hardcoded" my $acard=0; my $rec_ctrl="Aux,0"; @@ -25,7 +25,7 @@ my $vbitrate=1500; my $abitrate=224; ############################################## -# Those stuff bellow are NTSC / PAL-M specific +# Those stuff below are NTSC / PAL-M specific my $list="/usr/share/xawtv/ntsc-cable.list"; my $fps=30000/1001; diff --git a/v4l2-apps/util/xc3028-firmware/Makefile b/v4l2-apps/util/xc3028-firmware/Makefile index 102f712fd..6894e984e 100644 --- a/v4l2-apps/util/xc3028-firmware/Makefile +++ b/v4l2-apps/util/xc3028-firmware/Makefile @@ -1,6 +1,6 @@ # Makefile for linuxtv.org v4l2-apps/util/xc3028-firmware -CPPFLAGS += -I../../../linux/include +CPPFLAGS += -I../../include binaries = firmware-tool |