This is an automatic generated email to let you know that the following patch were queued at the http://git.linuxtv.org/xawtv3.git tree:
Subject: Add a libv4l driver Author: Mauro Carvalho Chehab <[email protected]> Date: Sat Jan 29 10:56:57 2011 -0200 Signed-off-by: Mauro Carvalho Chehab <[email protected]> Makefile.in | 1 + autogen.sh | 1 + configure.ac | 9 + libng/plugins/Subdir.mk | 10 + libng/plugins/drv0-libv4l.c | 1078 +++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 1099 insertions(+), 0 deletions(-) --- http://git.linuxtv.org/xawtv3.git?a=commitdiff;h=1d067df37c47ffacc6f7c6c25d7e2d57d93b29df diff --git a/Makefile.in b/Makefile.in index affc37e..8e1f80e 100644 --- a/Makefile.in +++ b/Makefile.in @@ -63,6 +63,7 @@ FOUND_OS := @FOUND_OS@ FOUND_X11 := @FOUND_X11@ FOUND_ZVBI := @FOUND_ZVBI@ USE_MMX := @USE_MMX@ +LIBV4L := @LIBV4L@ # build final cflags CFLAGS := @CFLAGS@ diff --git a/autogen.sh b/autogen.sh index 2bea23f..b30380c 100755 --- a/autogen.sh +++ b/autogen.sh @@ -5,3 +5,4 @@ autoconf autoheader rm -rf autom4te.cache cp "$inst" . +./configure diff --git a/configure.ac b/configure.ac index b057f7f..5407e06 100644 --- a/configure.ac +++ b/configure.ac @@ -56,6 +56,7 @@ AC_SUBST(FOUND_OS) AC_SUBST(FOUND_X11) AC_SUBST(FOUND_GL) AC_SUBST(FOUND_ZVBI) +AC_SUBST(LIBV4L) FOUND_AALIB="no" FOUND_ALSA="no" FOUND_DV="no" @@ -65,6 +66,7 @@ FOUND_OS="unknown" FOUND_X11="no" FOUND_GL="no" FOUND_ZVBI="no" +LIBV4L="no" case "`uname -s`" in Linux) @@ -169,6 +171,13 @@ AC_SUBST(LDLIBS) AC_SUBST(XFT_FLAGS) AC_SUBST(XFT_LIBS) +AC_CHECK_LIB(v4l2, v4l2_fd_open, LIBV4L="yes",,) +if test "$LIBV4L" != "yes"; then + echo "Oops: libv4l library not found. This is needed to use most webcams." + echo "Note: to compile stuff just the library packages are not enough," + echo " you need also the *-devel packages." +fi + AC_CHECK_LIB(jpeg, jpeg_start_compress, JPEG="found",,) if test "$JPEG" != "found"; then echo "Oops: jpeg library not found. You need this one, please install." diff --git a/libng/plugins/Subdir.mk b/libng/plugins/Subdir.mk index 6c11d8f..b892c5b 100644 --- a/libng/plugins/Subdir.mk +++ b/libng/plugins/Subdir.mk @@ -23,6 +23,10 @@ TARGETS-plugins += \ libng/plugins/drv0-v4l2.so \ libng/plugins/snd-oss.so endif +ifeq ($(LIBV4L),yes) +TARGETS-plugins += \ + libng/plugins/drv0-libv4l.so +endif ifeq ($(FOUND_OS),bsd) TARGETS-plugins += \ libng/plugins/drv0-bsd.so \ @@ -39,6 +43,7 @@ libng/plugins/read-qt.so : LDLIBS := $(QT_LIBS) libng/plugins/write-qt.so : LDLIBS := $(QT_LIBS) libng/plugins/read-dv.so : LDLIBS := $(DV_LIBS) libng/plugins/write-dv.so : LDLIBS := $(DV_LIBS) +libng/plugins/drv0-libv4l.so: LDLIBS := -lv4l2 # global targets all:: $(TARGETS-plugins) @@ -70,6 +75,11 @@ libng/plugins/drv0-v4l2.so: \ libng/plugins/struct-v4l2.o \ libng/plugins/struct-dump.o +libng/plugins/drv0-libv4l.so: \ + libng/plugins/drv0-libv4l.o \ + libng/plugins/struct-v4l2.o \ + libng/plugins/struct-dump.o + libng/plugins/struct-dump.o: structs/struct-dump.c @$(echo_compile_c) @$(compile_c) diff --git a/libng/plugins/drv0-libv4l.c b/libng/plugins/drv0-libv4l.c new file mode 100644 index 0000000..48e37b8 --- /dev/null +++ b/libng/plugins/drv0-libv4l.c @@ -0,0 +1,1078 @@ +/* + * interface to the libv4l driver + * + * (c) 1998-2002 Gerd Knorr <[email protected]> + * + * Ported to libv4l by Hans de Goede <[email protected]> + */ +#include "config.h" + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <math.h> +#include <errno.h> +#include <fcntl.h> +#include <string.h> +#include <signal.h> +#include <sys/types.h> +#include <sys/time.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <pthread.h> + +#include <asm/types.h> /* XXX glibc */ +#include "videodev2.h" + +#include "grab-ng.h" + +#include "struct-dump.h" +#include "struct-v4l2.h" + +#include <libv4l2.h> + +/* ---------------------------------------------------------------------- */ + +/* open+close */ +static void* v4l2_open_handle(char *device); +static int v4l2_close_handle(void *handle); + +/* attributes */ +static char* v4l2_devname(void *handle); +static int v4l2_flags(void *handle); +static struct ng_attribute* v4l2_attrs(void *handle); +static int v4l2_read_attr(struct ng_attribute*); +static void v4l2_write_attr(struct ng_attribute*, int val); + +/* overlay */ +static int v4l2_setupfb(void *handle, struct ng_video_fmt *fmt, void *base); +static int v4l2_overlay(void *handle, struct ng_video_fmt *fmt, int x, int y, + struct OVERLAY_CLIP *oc, int count, int aspect); + +/* capture video */ +static int v4l2_setformat(void *handle, struct ng_video_fmt *fmt); +static int v4l2_startvideo(void *handle, int fps, unsigned int buffers); +static void v4l2_stopvideo(void *handle); +static struct ng_video_buf* v4l2_nextframe(void *handle); +static struct ng_video_buf* v4l2_getimage(void *handle); + +/* tuner */ +static unsigned long v4l2_getfreq(void *handle); +static void v4l2_setfreq(void *handle, unsigned long freq); +static int v4l2_tuned(void *handle); + +/* ---------------------------------------------------------------------- */ + +#define WANTED_BUFFERS 32 + +#define MAX_INPUT 16 +#define MAX_NORM 16 +#define MAX_FORMAT 32 +#define MAX_CTRL 32 + +struct v4l2_handle { + int fd; + + /* device descriptions */ + int ninputs,nstds,nfmts; + struct v4l2_capability cap; + struct v4l2_streamparm streamparm; + struct v4l2_input inp[MAX_INPUT]; + struct v4l2_standard std[MAX_NORM]; + struct v4l2_fmtdesc fmt[MAX_FORMAT]; + struct v4l2_queryctrl ctl[MAX_CTRL*2]; + + /* attributes */ + int nattr; + struct ng_attribute *attr; + + /* capture */ + int fps,first; + long long start; + struct v4l2_format fmt_v4l2; + struct ng_video_fmt fmt_me; + struct v4l2_requestbuffers reqbufs; + struct v4l2_buffer buf_v4l2[WANTED_BUFFERS]; + int buf_v4l2_size[WANTED_BUFFERS]; + struct ng_video_buf buf_me[WANTED_BUFFERS]; + unsigned int queue,waiton; + + /* overlay */ + struct v4l2_framebuffer ov_fb; + struct v4l2_format ov_win; + struct v4l2_clip ov_clips[256]; +#if 0 + enum v4l2_field ov_fields; +#endif + int ov_error; + int ov_enabled; + int ov_on; +}; + +/* ---------------------------------------------------------------------- */ + +struct ng_vid_driver libv4l_driver = { + name: "libv4l", + open: v4l2_open_handle, + close: v4l2_close_handle, + + get_devname: v4l2_devname, + capabilities: v4l2_flags, + list_attrs: v4l2_attrs, + + setupfb: v4l2_setupfb, + overlay: v4l2_overlay, + + setformat: v4l2_setformat, + startvideo: v4l2_startvideo, + stopvideo: v4l2_stopvideo, + nextframe: v4l2_nextframe, + getimage: v4l2_getimage, + + getfreq: v4l2_getfreq, + setfreq: v4l2_setfreq, + is_tuned: v4l2_tuned, +}; + +static __u32 xawtv_pixelformat[VIDEO_FMT_COUNT] = { + [ VIDEO_RGB08 ] = V4L2_PIX_FMT_HI240, + [ VIDEO_GRAY ] = V4L2_PIX_FMT_GREY, + [ VIDEO_RGB15_LE ] = V4L2_PIX_FMT_RGB555, + [ VIDEO_RGB16_LE ] = V4L2_PIX_FMT_RGB565, + [ VIDEO_RGB15_BE ] = V4L2_PIX_FMT_RGB555X, + [ VIDEO_RGB16_BE ] = V4L2_PIX_FMT_RGB565X, + [ VIDEO_BGR24 ] = V4L2_PIX_FMT_BGR24, + [ VIDEO_BGR32 ] = V4L2_PIX_FMT_BGR32, + [ VIDEO_RGB24 ] = V4L2_PIX_FMT_RGB24, + [ VIDEO_YUYV ] = V4L2_PIX_FMT_YUYV, + [ VIDEO_UYVY ] = V4L2_PIX_FMT_UYVY, + [ VIDEO_YUV422P ] = V4L2_PIX_FMT_YUV422P, + [ VIDEO_YUV420P ] = V4L2_PIX_FMT_YUV420, +}; + +static struct STRTAB stereo[] = { + { V4L2_TUNER_MODE_MONO, "mono" }, + { V4L2_TUNER_MODE_STEREO, "stereo" }, + { V4L2_TUNER_MODE_LANG1, "lang1" }, + { V4L2_TUNER_MODE_LANG2, "lang2" }, + { -1, NULL }, +}; + +/* ---------------------------------------------------------------------- */ +/* debug output */ + +#define PREFIX "ioctl: " + +static int +xioctl(int fd, int cmd, void *arg, int mayfail) +{ + int rc; + + rc = v4l2_ioctl(fd,cmd,arg); + if (rc >= 0 && ng_debug < 2) + return rc; + if (mayfail && errno == mayfail && ng_debug < 2) + return rc; + print_ioctl(stderr,ioctls_v4l2,PREFIX,cmd,arg); + fprintf(stderr,": %s\n",(rc == 0) ? "ok" : strerror(errno)); + return rc; +} + +static void +print_bufinfo(struct v4l2_buffer *buf) +{ + static char *type[] = { + [V4L2_BUF_TYPE_VIDEO_CAPTURE] = "video-cap", + [V4L2_BUF_TYPE_VIDEO_OVERLAY] = "video-over", + [V4L2_BUF_TYPE_VIDEO_OUTPUT] = "video-out", + [V4L2_BUF_TYPE_VBI_CAPTURE] = "vbi-cap", + [V4L2_BUF_TYPE_VBI_OUTPUT] = "vbi-out", + }; + + fprintf(stderr,"v4l2: buf %d: %s 0x%x+%d, used %d\n", + buf->index, + buf->type < sizeof(type)/sizeof(char*) + ? type[buf->type] : "unknown", + buf->m.offset,buf->length,buf->bytesused); +} + +/* ---------------------------------------------------------------------- */ +/* helpers */ + +static void +get_device_capabilities(struct v4l2_handle *h) +{ + int i; + + for (h->ninputs = 0; h->ninputs < MAX_INPUT; h->ninputs++) { + h->inp[h->ninputs].index = h->ninputs; + if (-1 == xioctl(h->fd, VIDIOC_ENUMINPUT, &h->inp[h->ninputs], EINVAL)) + break; + } + for (h->nstds = 0; h->nstds < MAX_NORM; h->nstds++) { + h->std[h->nstds].index = h->nstds; + if (-1 == xioctl(h->fd, VIDIOC_ENUMSTD, &h->std[h->nstds], EINVAL)) + break; + } + for (h->nfmts = 0; h->nfmts < MAX_FORMAT; h->nfmts++) { + h->fmt[h->nfmts].index = h->nfmts; + h->fmt[h->nfmts].type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (-1 == xioctl(h->fd, VIDIOC_ENUM_FMT, &h->fmt[h->nfmts], EINVAL)) + break; + } + + h->streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + v4l2_ioctl(h->fd,VIDIOC_G_PARM,&h->streamparm); + + /* controls */ + for (i = 0; i < MAX_CTRL; i++) { + h->ctl[i].id = V4L2_CID_BASE+i; + if (-1 == xioctl(h->fd, VIDIOC_QUERYCTRL, &h->ctl[i], EINVAL) || + (h->ctl[i].flags & V4L2_CTRL_FLAG_DISABLED)) + h->ctl[i].id = -1; + } + for (i = 0; i < MAX_CTRL; i++) { + h->ctl[i+MAX_CTRL].id = V4L2_CID_PRIVATE_BASE+i; + if (-1 == xioctl(h->fd, VIDIOC_QUERYCTRL, &h->ctl[i+MAX_CTRL], EINVAL) || + (h->ctl[i+MAX_CTRL].flags & V4L2_CTRL_FLAG_DISABLED)) + h->ctl[i+MAX_CTRL].id = -1; + } +} + +static struct STRTAB * +build_norms(struct v4l2_handle *h) +{ + struct STRTAB *norms; + int i; + + norms = malloc(sizeof(struct STRTAB) * (h->nstds+1)); + for (i = 0; i < h->nstds; i++) { + norms[i].nr = i; + norms[i].str = h->std[i].name; + } + norms[i].nr = -1; + norms[i].str = NULL; + return norms; +} + +static struct STRTAB * +build_inputs(struct v4l2_handle *h) +{ + struct STRTAB *inputs; + int i; + + inputs = malloc(sizeof(struct STRTAB) * (h->ninputs+1)); + for (i = 0; i < h->ninputs; i++) { + inputs[i].nr = i; + inputs[i].str = h->inp[i].name; + } + inputs[i].nr = -1; + inputs[i].str = NULL; + return inputs; +} + +/* ---------------------------------------------------------------------- */ + +static struct V4L2_ATTR { + unsigned int id; + unsigned int v4l2; +} v4l2_attr[] = { + { ATTR_ID_VOLUME, V4L2_CID_AUDIO_VOLUME }, + { ATTR_ID_MUTE, V4L2_CID_AUDIO_MUTE }, + { ATTR_ID_COLOR, V4L2_CID_SATURATION }, + { ATTR_ID_BRIGHT, V4L2_CID_BRIGHTNESS }, + { ATTR_ID_HUE, V4L2_CID_HUE }, + { ATTR_ID_CONTRAST, V4L2_CID_CONTRAST }, +}; +#define NUM_ATTR (sizeof(v4l2_attr)/sizeof(struct V4L2_ATTR)) + +static struct STRTAB* +v4l2_menu(int fd, const struct v4l2_queryctrl *ctl) +{ + struct STRTAB *menu; + struct v4l2_querymenu item; + int i; + + menu = malloc(sizeof(struct STRTAB) * (ctl->maximum-ctl->minimum+2)); + for (i = ctl->minimum; i <= ctl->maximum; i++) { + item.id = ctl->id; + item.index = i; + if (-1 == xioctl(fd, VIDIOC_QUERYMENU, &item, 0)) { + free(menu); + return NULL; + } + menu[i-ctl->minimum].nr = i; + menu[i-ctl->minimum].str = strdup(item.name); + } + menu[i-ctl->minimum].nr = -1; + menu[i-ctl->minimum].str = NULL; + return menu; +} + +static void +v4l2_add_attr(struct v4l2_handle *h, struct v4l2_queryctrl *ctl, + int id, struct STRTAB *choices) +{ + static int private_ids = ATTR_ID_COUNT; + unsigned int i; + + h->attr = realloc(h->attr,(h->nattr+2) * sizeof(struct ng_attribute)); + memset(h->attr+h->nattr,0,sizeof(struct ng_attribute)*2); + if (ctl) { + for (i = 0; i < NUM_ATTR; i++) + if (v4l2_attr[i].v4l2 == ctl->id) + break; + if (i != NUM_ATTR) { + h->attr[h->nattr].id = v4l2_attr[i].id; + } else { + h->attr[h->nattr].id = private_ids++; + } + h->attr[h->nattr].name = ctl->name; + h->attr[h->nattr].priv = ctl; + h->attr[h->nattr].defval = ctl->default_value; + switch (ctl->type) { + case V4L2_CTRL_TYPE_INTEGER: + h->attr[h->nattr].type = ATTR_TYPE_INTEGER; + h->attr[h->nattr].defval = ctl->default_value; + h->attr[h->nattr].min = ctl->minimum; + h->attr[h->nattr].max = ctl->maximum; + break; + case V4L2_CTRL_TYPE_BOOLEAN: + h->attr[h->nattr].type = ATTR_TYPE_BOOL; + break; + case V4L2_CTRL_TYPE_MENU: + h->attr[h->nattr].type = ATTR_TYPE_CHOICE; + h->attr[h->nattr].choices = v4l2_menu(h->fd, ctl); + break; + default: + memset(h->attr+h->nattr,0,sizeof(struct ng_attribute)*2); + return; + } + } else { + /* for norms + inputs */ + h->attr[h->nattr].id = id; + if (-1 == h->attr[h->nattr].id) + h->attr[h->nattr].id = private_ids++; + h->attr[h->nattr].defval = 0; + h->attr[h->nattr].type = ATTR_TYPE_CHOICE; + h->attr[h->nattr].choices = choices; + } + if (h->attr[h->nattr].id < ATTR_ID_COUNT) + h->attr[h->nattr].name = ng_attr_to_desc[h->attr[h->nattr].id]; + + h->attr[h->nattr].read = v4l2_read_attr; + h->attr[h->nattr].write = v4l2_write_attr; + h->attr[h->nattr].handle = h; + h->nattr++; +} + +static int v4l2_read_attr(struct ng_attribute *attr) +{ + struct v4l2_handle *h = attr->handle; + const struct v4l2_queryctrl *ctl = attr->priv; + struct v4l2_control c; + struct v4l2_tuner tuner; + v4l2_std_id std; + int value = 0; + int i; + + if (NULL != ctl) { + c.id = ctl->id; + xioctl(h->fd,VIDIOC_G_CTRL,&c,0); + value = c.value; + + } else if (attr->id == ATTR_ID_NORM) { + value = -1; + xioctl(h->fd,VIDIOC_G_STD,&std,0); + for (i = 0; i < h->nstds; i++) + if (std & h->std[i].id) + value = i; + + } else if (attr->id == ATTR_ID_INPUT) { + xioctl(h->fd,VIDIOC_G_INPUT,&value,0); + + } else if (attr->id == ATTR_ID_AUDIO_MODE) { + memset(&tuner,0,sizeof(tuner)); + xioctl(h->fd,VIDIOC_G_TUNER,&tuner,0); + value = tuner.audmode; +#if 1 + if (ng_debug) { + fprintf(stderr,"v4l2: tuner cap:%s%s%s\n", + (tuner.capability&V4L2_TUNER_CAP_STEREO) ? " STEREO" : "", + (tuner.capability&V4L2_TUNER_CAP_LANG1) ? " LANG1" : "", + (tuner.capability&V4L2_TUNER_CAP_LANG2) ? " LANG2" : ""); + fprintf(stderr,"v4l2: tuner rxs:%s%s%s%s\n", + (tuner.rxsubchans&V4L2_TUNER_SUB_MONO) ? " MONO" : "", + (tuner.rxsubchans&V4L2_TUNER_SUB_STEREO) ? " STEREO" : "", + (tuner.rxsubchans&V4L2_TUNER_SUB_LANG1) ? " LANG1" : "", + (tuner.rxsubchans&V4L2_TUNER_SUB_LANG2) ? " LANG2" : ""); + fprintf(stderr,"v4l2: tuner cur:%s%s%s%s\n", + (tuner.audmode==V4L2_TUNER_MODE_MONO) ? " MONO" : "", + (tuner.audmode==V4L2_TUNER_MODE_STEREO) ? " STEREO" : "", + (tuner.audmode==V4L2_TUNER_MODE_LANG1) ? " LANG1" : "", + (tuner.audmode==V4L2_TUNER_MODE_LANG2) ? " LANG2" : ""); + } +#endif + } + return value; +} + +static void v4l2_write_attr(struct ng_attribute *attr, int value) +{ + struct v4l2_handle *h = attr->handle; + const struct v4l2_queryctrl *ctl = attr->priv; + struct v4l2_control c; + struct v4l2_tuner tuner; + + if (NULL != ctl) { + c.id = ctl->id; + c.value = value; + xioctl(h->fd,VIDIOC_S_CTRL,&c,0); + + } else if (attr->id == ATTR_ID_NORM) { + xioctl(h->fd,VIDIOC_S_STD,&h->std[value].id,0); + + } else if (attr->id == ATTR_ID_INPUT) { + xioctl(h->fd,VIDIOC_S_INPUT,&value,0); + + } else if (attr->id == ATTR_ID_AUDIO_MODE) { + memset(&tuner,0,sizeof(tuner)); + xioctl(h->fd,VIDIOC_G_TUNER,&tuner,0); + tuner.audmode = value; + xioctl(h->fd,VIDIOC_S_TUNER,&tuner,0); + } +} + +/* ---------------------------------------------------------------------- */ + +static void* +v4l2_open_handle(char *device) +{ + struct v4l2_handle *h; + int i, libv4l2_fd; + + h = malloc(sizeof(*h)); + if (NULL == h) + return NULL; + memset(h,0,sizeof(*h)); + + if (-1 == (h->fd = open(device, O_RDWR))) { + fprintf(stderr,"v4l2: open %s: %s\n",device,strerror(errno)); + goto err; + } + + /* Note the v4l2_xxx functions are designed so that if they get passed an + unknown fd, the will behave exactly as their regular xxx counterparts, so + if v4l2_fd_open fails, we continue as normal (missing the libv4l2 custom + cam format to normal formats conversion). Chances are big we will still + fail then though, as normally v4l2_fd_open only fails if the device is not + a v4l2 device. */ + libv4l2_fd = v4l2_fd_open(h->fd, 0); + if (libv4l2_fd != -1) + h->fd = libv4l2_fd; + + if (-1 == xioctl(h->fd,VIDIOC_QUERYCAP,&h->cap,EINVAL)) + goto err; + if (ng_debug) + fprintf(stderr, "v4l2: open\n"); + fcntl(h->fd,F_SETFD,FD_CLOEXEC); + if (ng_debug) + fprintf(stderr,"v4l2: device info:\n" + " %s %d.%d.%d / %s @ %s\n", + h->cap.driver, + (h->cap.version >> 16) & 0xff, + (h->cap.version >> 8) & 0xff, + h->cap.version & 0xff, + h->cap.card,h->cap.bus_info); + get_device_capabilities(h); + + /* attributes */ + v4l2_add_attr(h, NULL, ATTR_ID_NORM, build_norms(h)); + v4l2_add_attr(h, NULL, ATTR_ID_INPUT, build_inputs(h)); + if (h->cap.capabilities & V4L2_CAP_TUNER) + v4l2_add_attr(h, NULL, ATTR_ID_AUDIO_MODE, stereo); + for (i = 0; i < MAX_CTRL*2; i++) { + if (h->ctl[i].id == UNSET) + continue; + v4l2_add_attr(h, &h->ctl[i], 0, NULL); + } + + /* capture buffers */ + for (i = 0; i < WANTED_BUFFERS; i++) { + ng_init_video_buf(h->buf_me+i); + h->buf_me[i].release = ng_wakeup_video_buf; + } + + return h; + + err: + if (h->fd != -1) + v4l2_close(h->fd); + if (h) + free(h); + return NULL; +} + +static int +v4l2_close_handle(void *handle) +{ + struct v4l2_handle *h = handle; + + if (ng_debug) + fprintf(stderr, "v4l2: close\n"); + + v4l2_close(h->fd); + free(h); + return 0; +} + +static char* +v4l2_devname(void *handle) +{ + struct v4l2_handle *h = handle; + return h->cap.card; +} + +static int v4l2_flags(void *handle) +{ + struct v4l2_handle *h = handle; + int ret = 0; + + if (h->cap.capabilities & V4L2_CAP_VIDEO_OVERLAY && !h->ov_error) + ret |= CAN_OVERLAY; + if (h->cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) + ret |= CAN_CAPTURE; + if (h->cap.capabilities & V4L2_CAP_TUNER) + ret |= CAN_TUNE; + return ret; +} + +static struct ng_attribute* v4l2_attrs(void *handle) +{ + struct v4l2_handle *h = handle; + return h->attr; +} + +/* ---------------------------------------------------------------------- */ + +static unsigned long +v4l2_getfreq(void *handle) +{ + struct v4l2_handle *h = handle; + struct v4l2_frequency f; + + memset(&f,0,sizeof(f)); + xioctl(h->fd, VIDIOC_G_FREQUENCY, &f, 0); + return f.frequency; +} + +static void +v4l2_setfreq(void *handle, unsigned long freq) +{ + struct v4l2_handle *h = handle; + struct v4l2_frequency f; + + if (ng_debug) + fprintf(stderr,"v4l2: freq: %.3f\n",(float)freq/16); + memset(&f,0,sizeof(f)); + f.type = V4L2_TUNER_ANALOG_TV; + f.frequency = freq; + xioctl(h->fd, VIDIOC_S_FREQUENCY, &f, 0); +} + +static int +v4l2_tuned(void *handle) +{ + struct v4l2_handle *h = handle; + struct v4l2_tuner tuner; + + usleep(10000); + memset(&tuner,0,sizeof(tuner)); + if (-1 == xioctl(h->fd,VIDIOC_G_TUNER,&tuner,0)) + return 0; + return tuner.signal ? 1 : 0; +} + +/* ---------------------------------------------------------------------- */ +/* overlay */ + +static int +v4l2_setupfb(void *handle, struct ng_video_fmt *fmt, void *base) +{ + struct v4l2_handle *h = handle; + + if (-1 == xioctl(h->fd, VIDIOC_G_FBUF, &h->ov_fb, 0)) + return -1; + + /* double-check settings */ + if ((NULL != base && h->ov_fb.base != base) || h->ov_fb.base == NULL) { + fprintf(stderr,"v4l2: WARNING: framebuffer base address mismatch\n"); + fprintf(stderr,"v4l2: me=%p v4l=%p\n",base,h->ov_fb.base); + h->ov_error = 1; + return -1; + } + if (h->ov_fb.fmt.width != fmt->width || + h->ov_fb.fmt.height != fmt->height) { + fprintf(stderr,"v4l2: WARNING: framebuffer size mismatch\n"); + fprintf(stderr,"v4l2: me=%dx%d v4l=%dx%d\n", + fmt->width,fmt->height,h->ov_fb.fmt.width,h->ov_fb.fmt.height); + h->ov_error = 1; + return -1; + } + if (fmt->bytesperline > 0 && + fmt->bytesperline != h->ov_fb.fmt.bytesperline) { + fprintf(stderr,"v4l2: WARNING: framebuffer bpl mismatch\n"); + fprintf(stderr,"v4l2: me=%d v4l=%d\n", + fmt->bytesperline,h->ov_fb.fmt.bytesperline); + h->ov_error = 1; + return -1; + } +#if 0 + if (h->ov_fb.fmt.pixelformat != xawtv_pixelformat[fmt->fmtid]) { + fprintf(stderr,"v4l2: WARNING: framebuffer format mismatch\n"); + fprintf(stderr,"v4l2: me=%c%c%c%c [%s] v4l=%c%c%c%c\n", + xawtv_pixelformat[fmt->fmtid] & 0xff, + (xawtv_pixelformat[fmt->fmtid] >> 8) & 0xff, + (xawtv_pixelformat[fmt->fmtid] >> 16) & 0xff, + (xawtv_pixelformat[fmt->fmtid] >> 24) & 0xff, + ng_vfmt_to_desc[fmt->fmtid], + h->ov_fb.fmt.pixelformat & 0xff, + (h->ov_fb.fmt.pixelformat >> 8) & 0xff, + (h->ov_fb.fmt.pixelformat >> 16) & 0xff, + (h->ov_fb.fmt.pixelformat >> 24) & 0xff); + h->ov_error = 1; + return -1; + } +#endif + return 0; +} + +static int +v4l2_overlay(void *handle, struct ng_video_fmt *fmt, int x, int y, + struct OVERLAY_CLIP *oc, int count, int aspect) +{ + struct v4l2_handle *h = handle; + struct v4l2_format win; + int rc,i; + + if (h->ov_error) + return -1; + + if (NULL == fmt) { + if (ng_debug) + fprintf(stderr,"v4l2: overlay off\n"); + if (h->ov_enabled) { + h->ov_enabled = 0; + h->ov_on = 0; + xioctl(h->fd, VIDIOC_OVERLAY, &h->ov_on, 0); + } + return 0; + } + + if (ng_debug) + fprintf(stderr,"v4l2: overlay win=%dx%d+%d+%d, %d clips\n", + fmt->width,fmt->height,x,y,count); + memset(&win,0,sizeof(win)); + win.type = V4L2_BUF_TYPE_VIDEO_OVERLAY; + win.fmt.win.w.left = x; + win.fmt.win.w.top = y; + win.fmt.win.w.width = fmt->width; + win.fmt.win.w.height = fmt->height; + + /* check against max. size */ + xioctl(h->fd,VIDIOC_TRY_FMT,&win,0); + if (win.fmt.win.w.width != (int)fmt->width) + win.fmt.win.w.left = x + (fmt->width - win.fmt.win.w.width)/2; + if (win.fmt.win.w.height != (int)fmt->height) + win.fmt.win.w.top = y + (fmt->height - win.fmt.win.w.height)/2; + if (aspect) + ng_ratio_fixup(&win.fmt.win.w.width,&win.fmt.win.w.height, + &win.fmt.win.w.left,&win.fmt.win.w.top); + + /* fixups */ + ng_check_clipping(win.fmt.win.w.width, win.fmt.win.w.height, + x - win.fmt.win.w.left, y - win.fmt.win.w.top, + oc, &count); + + h->ov_win = win; + if (h->ov_fb.capability & V4L2_FBUF_CAP_LIST_CLIPPING) { + h->ov_win.fmt.win.clips = h->ov_clips; + h->ov_win.fmt.win.clipcount = count; + + for (i = 0; i < count; i++) { + h->ov_clips[i].next = (i+1 == count) ? NULL : &h->ov_clips[i+1]; + h->ov_clips[i].c.left = oc[i].x1; + h->ov_clips[i].c.top = oc[i].y1; + h->ov_clips[i].c.width = oc[i].x2-oc[i].x1; + h->ov_clips[i].c.height = oc[i].y2-oc[i].y1; + } + } +#if 0 + if (h->ov_fb.flags & V4L2_FBUF_FLAG_CHROMAKEY) { + h->ov_win.chromakey = 0; /* FIXME */ + } +#endif + rc = xioctl(h->fd, VIDIOC_S_FMT, &h->ov_win, 0); + + h->ov_enabled = (0 == rc) ? 1 : 0; + h->ov_on = (0 == rc) ? 1 : 0; + xioctl(h->fd, VIDIOC_OVERLAY, &h->ov_on, 0); + + return 0; +} + +/* ---------------------------------------------------------------------- */ +/* capture helpers */ + +static int +v4l2_queue_buffer(struct v4l2_handle *h) +{ + int frame = h->queue % h->reqbufs.count; + int rc; + + if (0 != h->buf_me[frame].refcount) { + if (0 != h->queue - h->waiton) + return -1; + fprintf(stderr,"v4l2: waiting for a free buffer\n"); + ng_waiton_video_buf(h->buf_me+frame); + } + + rc = xioctl(h->fd,VIDIOC_QBUF,&h->buf_v4l2[frame], 0); + if (0 == rc) + h->queue++; + return rc; +} + +static void +v4l2_queue_all(struct v4l2_handle *h) +{ + for (;;) { + if (h->queue - h->waiton >= h->reqbufs.count) + return; + if (0 != v4l2_queue_buffer(h)) + return; + } +} + +static int +v4l2_waiton(struct v4l2_handle *h) +{ + struct v4l2_buffer buf; + struct timeval tv; + fd_set rdset; + + /* wait for the next frame */ + again: + tv.tv_sec = 5; + tv.tv_usec = 0; + FD_ZERO(&rdset); + FD_SET(h->fd, &rdset); + switch (select(h->fd + 1, &rdset, NULL, NULL, &tv)) { + case -1: + if (EINTR == errno) + goto again; + perror("v4l2: select"); + return -1; + case 0: + fprintf(stderr,"v4l2: oops: select timeout\n"); + return -1; + } + + /* 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++; + h->buf_v4l2[buf.index] = buf; + +#if 0 + if (1) { + /* for driver debugging */ + static const char *fn[] = { + "any", "none", "top", "bottom", + "interlaced", "tb", "bt", "alternate", + }; + static struct timeval last; + signed long diff; + + diff = (buf.timestamp.tv_sec - last.tv_sec) * 1000000; + diff += buf.timestamp.tv_usec - last.tv_usec; + fprintf(stderr,"\tdiff %6.1f ms buf %d field %d [%s]\n", + diff/1000.0, buf.index, buf.field, fn[buf.field%8]); + last = buf.timestamp; + } +#endif + + return buf.index; +} + +static int +v4l2_start_streaming(struct v4l2_handle *h, int buffers) +{ + int disable_overlay = 0; + unsigned int i; + + /* setup buffers */ + h->reqbufs.count = buffers; + h->reqbufs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + h->reqbufs.memory = V4L2_MEMORY_MMAP; + if (-1 == xioctl(h->fd, VIDIOC_REQBUFS, &h->reqbufs, 0)) + return -1; + for (i = 0; i < h->reqbufs.count; i++) { + h->buf_v4l2[i].index = i; + h->buf_v4l2[i].type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + h->buf_v4l2[i].memory = V4L2_MEMORY_MMAP; + if (-1 == xioctl(h->fd, VIDIOC_QUERYBUF, &h->buf_v4l2[i], 0)) + return -1; + h->buf_v4l2_size[i] = h->buf_v4l2[i].length; + 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].data = v4l2_mmap(NULL, h->buf_v4l2[i].length, + PROT_READ | PROT_WRITE, MAP_SHARED, + h->fd, h->buf_v4l2[i].m.offset); + if (MAP_FAILED == h->buf_me[i].data) { + perror("mmap"); + return -1; + } + if (ng_debug) + print_bufinfo(&h->buf_v4l2[i]); + } + + /* queue up all buffers */ + v4l2_queue_all(h); + + try_again: + /* turn off preview (if needed) */ + if (disable_overlay) { + h->ov_on = 0; + xioctl(h->fd, VIDIOC_OVERLAY, &h->ov_on, 0); + if (ng_debug) + fprintf(stderr,"v4l2: overlay off (start_streaming)\n"); + } + + /* start capture */ + if (-1 == xioctl(h->fd,VIDIOC_STREAMON,&h->fmt_v4l2.type, + h->ov_on ? EBUSY : 0)) { + if (h->ov_on && errno == EBUSY) { + disable_overlay = 1; + goto try_again; + } + return -1; + } + return 0; +} + +static void +v4l2_stop_streaming(struct v4l2_handle *h) +{ + unsigned int i; + + /* stop capture */ + if (-1 == v4l2_ioctl(h->fd,VIDIOC_STREAMOFF,&h->fmt_v4l2.type)) + perror("ioctl VIDIOC_STREAMOFF"); + + /* free buffers */ + for (i = 0; i < h->reqbufs.count; i++) { + if (0 != h->buf_me[i].refcount) + ng_waiton_video_buf(&h->buf_me[i]); + if (ng_debug) + print_bufinfo(&h->buf_v4l2[i]); + if (-1 == v4l2_munmap(h->buf_me[i].data, h->buf_v4l2_size[i])) + perror("munmap"); + } + h->queue = 0; + h->waiton = 0; + + /* unrequest buffers (only needed for some drivers) */ + h->reqbufs.count = 0; + xioctl(h->fd, VIDIOC_REQBUFS, &h->reqbufs, EINVAL); + + /* turn on preview (if needed) */ + if (h->ov_on != h->ov_enabled) { + h->ov_on = h->ov_enabled; + xioctl(h->fd, VIDIOC_OVERLAY, &h->ov_on, 0); + if (ng_debug) + fprintf(stderr,"v4l2: overlay on (stop_streaming)\n"); + } +} + +/* ---------------------------------------------------------------------- */ +/* capture interface */ + +/* set capture parameters */ +static int +v4l2_setformat(void *handle, struct ng_video_fmt *fmt) +{ + struct v4l2_handle *h = handle; + + h->fmt_v4l2.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + h->fmt_v4l2.fmt.pix.pixelformat = xawtv_pixelformat[fmt->fmtid]; + h->fmt_v4l2.fmt.pix.width = fmt->width; + h->fmt_v4l2.fmt.pix.height = fmt->height; + h->fmt_v4l2.fmt.pix.field = V4L2_FIELD_ANY; + //h->fmt_v4l2.fmt.pix.field = V4L2_FIELD_ALTERNATE; + if (fmt->bytesperline != fmt->width * ng_vfmt_to_depth[fmt->fmtid]/8) + h->fmt_v4l2.fmt.pix.bytesperline = fmt->bytesperline; + else + h->fmt_v4l2.fmt.pix.bytesperline = 0; + + if (-1 == xioctl(h->fd, VIDIOC_S_FMT, &h->fmt_v4l2, EINVAL)) + return -1; + if (h->fmt_v4l2.fmt.pix.pixelformat != xawtv_pixelformat[fmt->fmtid]) + return -1; + fmt->width = h->fmt_v4l2.fmt.pix.width; + fmt->height = h->fmt_v4l2.fmt.pix.height; + fmt->bytesperline = h->fmt_v4l2.fmt.pix.bytesperline; + /* struct v4l2_format.fmt.pix.bytesperline is bytesperline for the + main plane for planar formats, where as we want it to be the total + bytesperline for all planes */ + switch (fmt->fmtid) { + case VIDEO_YUV422P: + fmt->bytesperline *= 2; + break; + case VIDEO_YUV420P: + fmt->bytesperline = fmt->bytesperline * 3 / 2; + break; + } + if (0 == fmt->bytesperline) + fmt->bytesperline = fmt->width * ng_vfmt_to_depth[fmt->fmtid] / 8; + h->fmt_me = *fmt; + if (ng_debug) + fprintf(stderr,"v4l2: new capture params (%dx%d, %c%c%c%c, %d byte)\n", + fmt->width,fmt->height, + h->fmt_v4l2.fmt.pix.pixelformat & 0xff, + (h->fmt_v4l2.fmt.pix.pixelformat >> 8) & 0xff, + (h->fmt_v4l2.fmt.pix.pixelformat >> 16) & 0xff, + (h->fmt_v4l2.fmt.pix.pixelformat >> 24) & 0xff, + h->fmt_v4l2.fmt.pix.sizeimage); + return 0; +} + +/* start/stop video */ +static int +v4l2_startvideo(void *handle, int fps, unsigned int buffers) +{ + struct v4l2_handle *h = handle; + + if (0 != h->fps) + fprintf(stderr,"v4l2_startvideo: oops: fps!=0\n"); + h->fps = fps; + h->first = 1; + h->start = 0; + + if (h->cap.capabilities & V4L2_CAP_STREAMING) + return v4l2_start_streaming(h,buffers); + return 0; +} + +static void +v4l2_stopvideo(void *handle) +{ + struct v4l2_handle *h = handle; + + if (0 == h->fps) + fprintf(stderr,"v4l2_stopvideo: oops: fps==0\n"); + h->fps = 0; + + if (h->cap.capabilities & V4L2_CAP_STREAMING) + v4l2_stop_streaming(h); +} + +/* read images */ +static struct ng_video_buf* +v4l2_nextframe(void *handle) +{ + struct v4l2_handle *h = handle; + struct ng_video_buf *buf = NULL; + int rc,size,frame = 0; + + if (h->cap.capabilities & V4L2_CAP_STREAMING) { + v4l2_queue_all(h); + frame = v4l2_waiton(h); + if (-1 == frame) + return NULL; + h->buf_me[frame].refcount++; + buf = &h->buf_me[frame]; + memset(&buf->info,0,sizeof(buf->info)); + buf->info.ts = ng_tofday_to_timestamp(&h->buf_v4l2[frame].timestamp); + } else { + size = h->fmt_me.bytesperline * h->fmt_me.height; + buf = ng_malloc_video_buf(&h->fmt_me,size); + rc = v4l2_read(h->fd,buf->data,size); + if (rc != size) { + if (-1 == rc) { + perror("v4l2: read"); + } else { + fprintf(stderr, "v4l2: read: rc=%d/size=%d\n",rc,size); + } + ng_release_video_buf(buf); + return NULL; + } + memset(&buf->info,0,sizeof(buf->info)); + buf->info.ts = ng_get_timestamp(); + } + + if (h->first) { + h->first = 0; + h->start = buf->info.ts; + if (ng_debug) + fprintf(stderr,"v4l2: start ts=%lld\n",h->start); + } + buf->info.ts -= h->start; + return buf; +} + +static struct ng_video_buf* +v4l2_getimage(void *handle) +{ + struct v4l2_handle *h = handle; + struct ng_video_buf *buf; + int size,frame,rc; + + size = h->fmt_me.bytesperline * h->fmt_me.height; + buf = ng_malloc_video_buf(&h->fmt_me,size); + if (h->cap.capabilities & V4L2_CAP_READWRITE) { + rc = v4l2_read(h->fd,buf->data,size); + if (-1 == rc && EBUSY == errno && h->ov_on) { + h->ov_on = 0; + xioctl(h->fd, VIDIOC_OVERLAY, &h->ov_on, 0); + rc = v4l2_read(h->fd,buf->data,size); + h->ov_on = 1; + xioctl(h->fd, VIDIOC_OVERLAY, &h->ov_on, 0); + } + if (rc != size) { + if (-1 == rc) { + perror("v4l2: read"); + } else { + fprintf(stderr, "v4l2: read: rc=%d/size=%d\n",rc,size); + } + ng_release_video_buf(buf); + return NULL; + } + } else { + if (-1 == v4l2_start_streaming(h,1)) { + v4l2_stop_streaming(h); + return NULL; + } + frame = v4l2_waiton(h); + if (-1 == frame) { + v4l2_stop_streaming(h); + return NULL; + } + memcpy(buf->data,h->buf_me[0].data,size); + v4l2_stop_streaming(h); + } + return buf; +} + +/* ---------------------------------------------------------------------- */ + +extern void ng_plugin_init(void); +void ng_plugin_init(void) +{ + ng_vid_driver_register(NG_PLUGIN_MAGIC,__FILE__,&libv4l_driver); +} _______________________________________________ linuxtv-commits mailing list [email protected] http://www.linuxtv.org/cgi-bin/mailman/listinfo/linuxtv-commits
