changeset 10536:527e978cb0f8

v4l2 support
author henry
date Thu, 07 Aug 2003 12:18:04 +0000
parents 784715bd119d
children 31f12f99118b
files libmpdemux/tvi_v4l2.c libmpdemux/videodev2.h
diffstat 2 files changed, 2484 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmpdemux/tvi_v4l2.c	Thu Aug 07 12:18:04 2003 +0000
@@ -0,0 +1,1622 @@
+/*
+**  Video 4 Linux 2 input
+**
+**  This file is part of MPlayer, see http://mplayerhq.hu/ for info.  
+**
+**  (c) 2003 Martin Olschewski <olschewski@zpr.uni-koeln.de>
+**  (c) 2003 Jindrich Makovicka <makovick@kmlinux.fjfi.cvut.cz>
+**  
+**  File licensed under the GPL, see http://www.fsf.org/ for more info.
+**
+**  Some ideas are based on works from
+**    Alex Beregszaszi <alex@naxine.org>
+**    Gerd Knorr <kraxel@bytesex.org>
+**
+**  CODE IS UNDER DEVELOPMENT, NO FEATURE REQUESTS PLEASE!
+*/
+
+/*
+
+known issues:
+- norm setting isn't consistent with tvi_v4l
+- the same for volume/bass/treble/balance
+
+*/
+
+#include "config.h"
+
+#if defined(USE_TV) && defined(HAVE_TV_V4L2)
+
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#ifdef HAVE_SYS_SYSINFO_H
+#include <sys/sysinfo.h>
+#endif
+#include "videodev2.h"
+#include "../mp_msg.h"
+#include "../libvo/img_format.h"
+#include "../libao2/afmt.h"
+#include "tv.h"
+#include "audio_in.h"
+
+/* information about this file */
+static tvi_info_t info = {
+    "Video 4 Linux 2 input",
+    "v4l2",
+    "Martin Olschewski <olschewski@zpr.uni-koeln.de>",
+    "first try, more to come ;-)"
+};
+
+struct map {
+    struct v4l2_buffer buf;
+    void   *addr;
+    size_t len;
+};
+
+#define BUFFER_COUNT 6
+
+/* private data */
+typedef struct {
+    /* video */
+    char			*video_dev;
+    int				video_fd;
+    int                         mp_format;
+    struct v4l2_capability	capability;
+    struct v4l2_input           input;
+    struct v4l2_format		format;
+    struct v4l2_standard	standard;
+    struct v4l2_tuner		tuner;
+    struct map			*map;
+    int				mapcount;
+    int				frames;
+    long long                   first_frame;
+    long long                   curr_frame;
+    /* audio video interleaving ;-) */
+    volatile int		streamon;
+    pthread_t			audio_grabber_thread;
+    pthread_mutex_t		skew_mutex;
+
+    /* 2nd level video buffers */
+    int                         first;
+    int                         immediate_mode;
+
+    int                         video_buffer_size_max;
+    volatile int                video_buffer_size_current;
+    unsigned char		**video_ringbuffer;
+    long long                   *video_timebuffer;
+    volatile int		video_head;
+    volatile int		video_tail;
+    volatile int		video_cnt;
+    pthread_t			video_grabber_thread;
+    pthread_mutex_t             video_buffer_mutex;
+
+    /* audio */
+    char			*audio_dev;
+    audio_in_t                  audio_in;
+
+    long long                   audio_start_time;
+    int                         audio_buffer_size;
+    int                         aud_skew_cnt;
+    unsigned char		*audio_ringbuffer;
+    long long			*audio_skew_buffer;
+    volatile int		audio_head;
+    volatile int		audio_tail;
+    volatile int		audio_cnt;
+    volatile long long          audio_skew;
+    volatile double             audio_skew_factor;
+    volatile long long          audio_skew_measure_time;
+    volatile int                audio_drop;
+    volatile int                shutdown;
+
+    double                      audio_secs_per_block;
+    long long                   audio_skew_total;
+    long			audio_recv_blocks_total;
+    long			audio_sent_blocks_total;
+} priv_t;
+
+#include "tvi_def.h"
+
+static void *audio_grabber(void *data);
+static void *video_grabber(void *data);
+
+/**********************************************************************\
+
+    Only few of the fourccs are the same in v4l2 and mplayer:
+
+    IMGFMT_YVU9 == V4L2_PIX_FMT_YVU410
+    IMGFMT_YV12 == V4L2_PIX_FMT_YVU420
+    IMGFMT_NV12 == V4L2_PIX_FMT_NV12
+    IMGFMT_422P == V4L2_PIX_FMT_YUV422P
+    IMGFMT_411P == V4L2_PIX_FMT_YUV411P
+    IMGFMT_UYVY == V4L2_PIX_FMT_UYVY
+    IMGFMT_Y41P == V4L2_PIX_FMT_Y41P
+
+    This may be an useful translation table for some others:
+
+    IMGFMT_RGB8  == V4L2_PIX_FMT_RGB332
+    IMGFMT_RGB15 == V4L2_PIX_FMT_RGB555
+    IMGFMT_RGB16 == V4L2_PIX_FMT_RGB565
+    IMGFMT_RGB24 == V4L2_PIX_FMT_RGB24
+    IMGFMT_RGB32 == V4L2_PIX_FMT_RGB32
+    IMGFMT_BGR24 == V4L2_PIX_FMT_BGR24
+    IMGFMT_BGR32 == V4L2_PIX_FMT_BGR32
+    IMGFMT_Y800  == V4L2_PIX_FMT_GREY
+    IMGFMT_IF09  == V4L2_PIX_FMT_YUV410
+    IMGFMT_I420  == V4L2_PIX_FMT_YUV420
+    IMGFMT_YUY2  == V4L2_PIX_FMT_YUYV
+
+\**********************************************************************/
+
+/*
+** Translate a mplayer fourcc to a video4linux2 pixel format.
+*/
+static int fcc_mp2vl(int fcc)
+{
+    switch (fcc) {
+    case IMGFMT_RGB8:	return V4L2_PIX_FMT_RGB332;
+    case IMGFMT_RGB15:	return V4L2_PIX_FMT_RGB555;
+    case IMGFMT_RGB16:	return V4L2_PIX_FMT_RGB565;
+    case IMGFMT_RGB24:	return V4L2_PIX_FMT_RGB24;
+    case IMGFMT_RGB32:	return V4L2_PIX_FMT_RGB32;
+    case IMGFMT_BGR24:	return V4L2_PIX_FMT_BGR24;
+    case IMGFMT_BGR32:	return V4L2_PIX_FMT_BGR32;
+    case IMGFMT_Y800:	return V4L2_PIX_FMT_GREY;
+    case IMGFMT_IF09:	return V4L2_PIX_FMT_YUV410;
+    case IMGFMT_I420:	return V4L2_PIX_FMT_YUV420;
+    case IMGFMT_YUY2:	return V4L2_PIX_FMT_YUYV;
+    case IMGFMT_YV12:	return V4L2_PIX_FMT_YUV420;
+    }
+    return fcc;
+}
+
+/*
+** Translate a video4linux2 fourcc aka pixel format to mplayer.
+*/
+static int fcc_vl2mp(int fcc)
+{
+    switch (fcc) {
+    case V4L2_PIX_FMT_RGB332:	return IMGFMT_RGB8;
+    case V4L2_PIX_FMT_RGB555:	return IMGFMT_RGB15;
+    case V4L2_PIX_FMT_RGB565:	return IMGFMT_RGB16;
+    case V4L2_PIX_FMT_RGB24:	return IMGFMT_RGB24;
+    case V4L2_PIX_FMT_RGB32:	return IMGFMT_RGB32;
+    case V4L2_PIX_FMT_BGR24:	return IMGFMT_BGR24;
+    case V4L2_PIX_FMT_BGR32:	return IMGFMT_BGR32;
+    case V4L2_PIX_FMT_GREY:		return IMGFMT_Y800;
+    case V4L2_PIX_FMT_YUV410:	return IMGFMT_IF09;
+    case V4L2_PIX_FMT_YUV420:	return IMGFMT_I420;
+    case V4L2_PIX_FMT_YUYV:		return IMGFMT_YUY2;
+    }
+    return fcc;
+}
+
+/*
+** Translate a video4linux2 fourcc aka pixel format
+** to a human readable string.
+*/
+static char *pixfmt2name(int pixfmt)
+{
+    static char unknown[24];
+
+    switch (pixfmt) {
+    case V4L2_PIX_FMT_RGB332:	return "RGB332";
+    case V4L2_PIX_FMT_RGB555:	return "RGB555";
+    case V4L2_PIX_FMT_RGB565:	return "RGB565";
+    case V4L2_PIX_FMT_RGB555X:	return "RGB555X";
+    case V4L2_PIX_FMT_RGB565X:	return "RGB565X";
+    case V4L2_PIX_FMT_BGR24:	return "BGR24";
+    case V4L2_PIX_FMT_RGB24:	return "RGB24";
+    case V4L2_PIX_FMT_BGR32:	return "BGR32";
+    case V4L2_PIX_FMT_RGB32:	return "RGB32";
+    case V4L2_PIX_FMT_GREY:		return "GREY";
+    case V4L2_PIX_FMT_YVU410:	return "YVU410";
+    case V4L2_PIX_FMT_YVU420:	return "YVU420";
+    case V4L2_PIX_FMT_YUYV:		return "YUYV";
+    case V4L2_PIX_FMT_UYVY:		return "UYVY";
+/*	case V4L2_PIX_FMT_YVU422P:	return "YVU422P"; */
+/*	case V4L2_PIX_FMT_YVU411P:	return "YVU411P"; */
+    case V4L2_PIX_FMT_YUV422P:	return "YUV422P";
+    case V4L2_PIX_FMT_YUV411P:	return "YUV411P";
+    case V4L2_PIX_FMT_Y41P:		return "Y41P";
+    case V4L2_PIX_FMT_NV12:		return "NV12";
+    case V4L2_PIX_FMT_NV21:		return "NV21";
+    case V4L2_PIX_FMT_YUV410:	return "YUV410";
+    case V4L2_PIX_FMT_YUV420:	return "YUV420";
+    case V4L2_PIX_FMT_YYUV:		return "YYUV";
+    case V4L2_PIX_FMT_HI240:	return "HI240";
+    case V4L2_PIX_FMT_WNVA:		return "WNVA";
+    }
+    sprintf(unknown, "unknown (0x%x)", pixfmt);
+    return unknown;
+}
+
+
+/*
+** Gives the depth of a video4linux2 fourcc aka pixel format in bits.
+*/
+static int pixfmt2depth(int pixfmt)
+{
+    switch (pixfmt) {
+    case V4L2_PIX_FMT_RGB332:
+	return 8;
+    case V4L2_PIX_FMT_RGB555:
+    case V4L2_PIX_FMT_RGB565:
+    case V4L2_PIX_FMT_RGB555X:
+    case V4L2_PIX_FMT_RGB565X:
+	return 16;
+    case V4L2_PIX_FMT_BGR24:
+    case V4L2_PIX_FMT_RGB24:
+	return 24;
+    case V4L2_PIX_FMT_BGR32:
+    case V4L2_PIX_FMT_RGB32:
+	return 32;
+    case V4L2_PIX_FMT_GREY:
+	return 8;
+    case V4L2_PIX_FMT_YVU410:
+	return 9;
+    case V4L2_PIX_FMT_YVU420:
+	return 12;
+    case V4L2_PIX_FMT_YUYV:
+    case V4L2_PIX_FMT_UYVY:
+    case V4L2_PIX_FMT_YUV422P:
+    case V4L2_PIX_FMT_YUV411P:
+	return 16;
+    case V4L2_PIX_FMT_Y41P:
+    case V4L2_PIX_FMT_NV12:
+    case V4L2_PIX_FMT_NV21:
+	return 12;
+    case V4L2_PIX_FMT_YUV410:
+	return 9;
+    case V4L2_PIX_FMT_YUV420:
+	return 12;
+    case V4L2_PIX_FMT_YYUV:
+	return 16;
+    case V4L2_PIX_FMT_HI240:
+	return 8;
+
+    }
+    return 0;
+}
+
+static int amode2v4l(int amode) 
+{
+    switch (amode) {
+    case 0:
+	return V4L2_TUNER_MODE_MONO;
+    case 1:
+	return V4L2_TUNER_MODE_STEREO;
+    case 2:
+	return V4L2_TUNER_MODE_LANG1;
+    case 3:
+	return V4L2_TUNER_MODE_LANG2;
+    default:
+	return -1;
+    }
+}
+
+
+// sets and sanitizes audio buffer/block sizes
+static void setup_audio_buffer_sizes(priv_t *priv)
+{
+    int bytes_per_sample = priv->audio_in.bytes_per_sample;
+    double fps = priv->standard.frameperiod.denominator /
+	priv->standard.frameperiod.numerator;
+    int seconds = priv->video_buffer_size_max/fps;
+
+    if (seconds < 5) seconds = 5;
+    if (seconds > 500) seconds = 500;
+
+    // make the audio buffer at least as the video buffer capacity (or 5 seconds) long
+    priv->audio_buffer_size = 1 + seconds*priv->audio_in.samplerate
+	*priv->audio_in.channels
+	*bytes_per_sample/priv->audio_in.blocksize;
+    if (priv->audio_buffer_size < 256) priv->audio_buffer_size = 256;
+
+    // make the skew buffer at least 1 second long
+    priv->aud_skew_cnt = 1 + 1*priv->audio_in.samplerate
+	*priv->audio_in.channels
+	*bytes_per_sample/priv->audio_in.blocksize;
+    if (priv->aud_skew_cnt < 16) priv->aud_skew_cnt = 16;
+
+    mp_msg(MSGT_TV, MSGL_V, "Audio capture - buffer %d blocks of %d bytes, skew average from %d meas.\n",
+	   priv->audio_buffer_size, priv->audio_in.blocksize, priv->aud_skew_cnt);
+}
+
+#if 0
+/*
+** the number of milliseconds elapsed between time0 and time1
+*/
+static size_t difftv(struct timeval time1, struct timeval time0)
+{
+    return	(time1.tv_sec  - time0.tv_sec)  * 1000 +
+	(time1.tv_usec - time0.tv_usec) / 1000;
+}
+#endif
+
+/*
+** Get current video capture format.
+*/
+static int getfmt(priv_t *priv)
+{
+    int i;
+
+    priv->format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+    if ((i = ioctl(priv->video_fd, VIDIOC_G_FMT, &priv->format)) < 0) {
+	mp_msg(MSGT_TV, MSGL_ERR, "%s: ioctl get format failed: %s\n",
+	       info.short_name, strerror(errno));
+    }
+    return i;
+}
+
+
+/*
+** Get current video capture standard.
+*/
+static int getstd(priv_t *priv)
+{
+    v4l2_std_id id;
+    int i=0;
+
+    if (ioctl(priv->video_fd, VIDIOC_G_STD, &id) < 0) {
+	mp_msg(MSGT_TV, MSGL_ERR, "%s: ioctl get standard failed: %s\n",
+	       info.short_name, strerror(errno));
+	return -1;
+    }
+    do {
+	priv->standard.index = i++;
+	if (ioctl(priv->video_fd, VIDIOC_ENUMSTD, &priv->standard) < 0) {
+	    return -1;
+	}
+    } while (priv->standard.id != id);
+    return 0;
+}
+
+/***********************************************************************\
+ *									*
+ *									*
+ *	Interface to mplayer						*
+ *									*
+ *									*
+\***********************************************************************/
+
+static int set_mute(priv_t *priv, int value) 
+{
+    struct v4l2_control control;
+    control.id = V4L2_CID_AUDIO_MUTE;
+    control.value = value;
+    if (ioctl(priv->video_fd, VIDIOC_S_CTRL, &control) < 0) {
+	mp_msg(MSGT_TV,MSGL_ERR,"%s: ioctl set mute failed: %s\n",
+	       info.short_name, strerror(errno));
+	return 0;
+    }
+    return 1;
+}
+
+/*
+** Mplayer uses values from -100 up to 100 for controls.
+** Here they are scaled to what the tv card needs and applied.
+*/
+static int set_control(priv_t *priv, struct v4l2_control *control, int val_signed) {
+    struct v4l2_queryctrl	qctrl;
+
+    qctrl.id = control->id;
+    if (ioctl(priv->video_fd, VIDIOC_QUERYCTRL, &qctrl) < 0) {
+	mp_msg(MSGT_TV, MSGL_ERR, "%s: ioctl query control failed: %s\n",
+	 info.short_name, strerror(errno));
+	return TVI_CONTROL_FALSE;
+    }
+
+    if (val_signed) {
+	if (control->value < 0) {
+	    control->value = qctrl.default_value + control->value *
+		(qctrl.default_value - qctrl.minimum) / 100;
+	} else {
+	    control->value = qctrl.default_value + control->value *
+		(qctrl.maximum - qctrl.default_value) / 100;
+	}
+    } else {
+	if (control->value < 50) {
+	    control->value = qctrl.default_value + (control->value-50) *
+		(qctrl.default_value - qctrl.minimum) / 50;
+	} else {
+	    control->value = qctrl.default_value + (control->value-50) *
+		(qctrl.maximum - qctrl.default_value) / 50;
+	}
+    }
+    
+
+    if (ioctl(priv->video_fd, VIDIOC_S_CTRL, control) < 0) {
+	mp_msg(MSGT_TV, MSGL_ERR,"%s: ioctl set %s %d failed: %s\n",
+	 info.short_name, qctrl.name, control->value, strerror(errno));
+	return TVI_CONTROL_FALSE;
+    }
+    mp_msg(MSGT_TV, MSGL_V, "%s: set %s: %d [%d, %d]\n", info.short_name,
+     qctrl.name, control->value, qctrl.minimum, qctrl.maximum);
+
+    return TVI_CONTROL_TRUE;
+}
+
+
+/*
+** Scale the control values back to what mplayer needs.
+*/
+static int get_control(priv_t *priv, struct v4l2_control *control, int val_signed) {
+    struct v4l2_queryctrl	qctrl;
+
+    qctrl.id = control->id;
+    if (ioctl(priv->video_fd, VIDIOC_QUERYCTRL, &qctrl) < 0) {
+	mp_msg(MSGT_TV, MSGL_ERR, "%s: ioctl query control failed: %s\n",
+	 info.short_name, strerror(errno));
+	return TVI_CONTROL_FALSE;
+    }
+
+    if (ioctl(priv->video_fd, VIDIOC_G_CTRL, control) < 0) {
+	mp_msg(MSGT_TV, MSGL_ERR,"%s: ioctl get %s failed: %s\n",
+	 info.short_name, qctrl.name, strerror(errno));
+	return TVI_CONTROL_FALSE;
+    }
+    mp_msg(MSGT_TV, MSGL_V, "%s: get %s: %d [%d, %d]\n", info.short_name,
+     qctrl.name, control->value, qctrl.minimum, qctrl.maximum);
+
+    if (val_signed) {
+	if (control->value < qctrl.default_value) {
+	    control->value = (control->value - qctrl.default_value) * 100 /
+		(qctrl.default_value - qctrl.minimum);
+	} else {
+	    control->value = (control->value - qctrl.default_value) * 100 /
+		(qctrl.maximum - qctrl.default_value);
+	}
+    } else {
+	if (control->value < qctrl.default_value) {
+	    control->value = (control->value - qctrl.default_value) * 50 /
+		(qctrl.default_value - qctrl.minimum) + 50;
+	} else {
+	    control->value = (control->value - qctrl.default_value) * 50 /
+		(qctrl.maximum - qctrl.default_value) + 50;
+	}
+    }
+
+    return TVI_CONTROL_TRUE;
+}
+
+static int control(priv_t *priv, int cmd, void *arg)
+{
+    struct v4l2_control control;
+    struct v4l2_frequency frequency;
+
+    switch(cmd) {
+    case TVI_CONTROL_IS_AUDIO:
+	if (tv_param_force_audio) return TVI_CONTROL_TRUE;
+	return priv->input.audioset ? TVI_CONTROL_TRUE: TVI_CONTROL_FALSE;
+    case TVI_CONTROL_IS_VIDEO:
+	return priv->capability.capabilities & V4L2_CAP_VIDEO_CAPTURE?
+	    TVI_CONTROL_TRUE: TVI_CONTROL_FALSE;
+    case TVI_CONTROL_IS_TUNER:
+	return priv->capability.capabilities & V4L2_CAP_TUNER?
+	    TVI_CONTROL_TRUE: TVI_CONTROL_FALSE;
+    case TVI_CONTROL_IMMEDIATE:
+	priv->immediate_mode = 1;
+	return TVI_CONTROL_TRUE;
+    case TVI_CONTROL_VID_GET_FPS:
+	*(float *)arg = priv->standard.frameperiod.denominator /
+	    priv->standard.frameperiod.numerator;
+	mp_msg(MSGT_TV, MSGL_V, "%s: get fps: %f\n", info.short_name,
+	       *(float *)arg);
+	return TVI_CONTROL_TRUE;
+    case TVI_CONTROL_VID_GET_BITS:
+	if (getfmt(priv) < 0) return TVI_CONTROL_FALSE;
+	*(int *)arg = pixfmt2depth(priv->format.fmt.pix.pixelformat);
+	mp_msg(MSGT_TV, MSGL_V, "%s: get depth: %d\n", info.short_name,
+	       *(int *)arg);
+	return TVI_CONTROL_TRUE;
+    case TVI_CONTROL_VID_GET_FORMAT:
+	if (getfmt(priv) < 0) return TVI_CONTROL_FALSE;
+	if (priv->mp_format == IMGFMT_YV12 && priv->format.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420) {
+	    *(int *)arg = IMGFMT_YV12;
+	} else {
+	    *(int *)arg = fcc_vl2mp(priv->format.fmt.pix.pixelformat);
+	}
+	mp_msg(MSGT_TV, MSGL_V, "%s: get format: %s\n", info.short_name,
+	       pixfmt2name(priv->format.fmt.pix.pixelformat));
+	return TVI_CONTROL_TRUE;
+    case TVI_CONTROL_VID_SET_FORMAT:
+	if (getfmt(priv) < 0) return TVI_CONTROL_FALSE;
+	priv->format.fmt.pix.pixelformat = fcc_mp2vl(*(int *)arg);
+	priv->format.fmt.pix.field = V4L2_FIELD_ANY;
+	    
+	priv->mp_format = *(int *)arg;
+	mp_msg(MSGT_TV, MSGL_V, "%s: set format: %s\n", info.short_name,
+	       pixfmt2name(priv->format.fmt.pix.pixelformat));
+	if (ioctl(priv->video_fd, VIDIOC_S_FMT, &priv->format) < 0) {
+	    mp_msg(MSGT_TV, MSGL_ERR, "%s: ioctl set format failed: %s\n",
+		   info.short_name, strerror(errno));
+	    return TVI_CONTROL_FALSE;
+	}
+	return TVI_CONTROL_TRUE;
+    case TVI_CONTROL_VID_GET_WIDTH:
+	if (getfmt(priv) < 0) return TVI_CONTROL_FALSE;
+	*(int *)arg = priv->format.fmt.pix.width;
+	mp_msg(MSGT_TV, MSGL_V, "%s: get width: %d\n", info.short_name,
+	       *(int *)arg);
+	return TVI_CONTROL_TRUE;
+    case TVI_CONTROL_VID_CHK_WIDTH:
+	return TVI_CONTROL_TRUE;
+    case TVI_CONTROL_VID_SET_WIDTH:
+	if (getfmt(priv) < 0) return TVI_CONTROL_FALSE;
+	priv->format.fmt.pix.width = *(int *)arg;
+	mp_msg(MSGT_TV, MSGL_V, "%s: set width: %d\n", info.short_name,
+	       *(int *)arg);
+	if (ioctl(priv->video_fd, VIDIOC_S_FMT, &priv->format) < 0) {
+	    mp_msg(MSGT_TV, MSGL_ERR, "%s: ioctl set width failed: %s\n",
+		   info.short_name, strerror(errno));
+	    return TVI_CONTROL_FALSE;
+	}
+	return TVI_CONTROL_TRUE;
+    case TVI_CONTROL_VID_GET_HEIGHT:
+	if (getfmt(priv) < 0) return TVI_CONTROL_FALSE;
+	*(int *)arg = priv->format.fmt.pix.height;
+	mp_msg(MSGT_TV, MSGL_V, "%s: get height: %d\n", info.short_name,
+	       *(int *)arg);
+	return TVI_CONTROL_TRUE;
+    case TVI_CONTROL_VID_CHK_HEIGHT:
+	return TVI_CONTROL_TRUE;
+    case TVI_CONTROL_VID_SET_HEIGHT:
+	if (getfmt(priv) < 0) return TVI_CONTROL_FALSE;
+	priv->format.fmt.pix.height = *(int *)arg;
+	mp_msg(MSGT_TV, MSGL_V, "%s: set height: %d\n", info.short_name,
+	       *(int *)arg);
+	if (ioctl(priv->video_fd, VIDIOC_S_FMT, &priv->format) < 0) {
+	    mp_msg(MSGT_TV, MSGL_ERR, "%s: ioctl set height failed: %s\n",
+		   info.short_name, strerror(errno));
+	    return TVI_CONTROL_FALSE;
+	}
+	return TVI_CONTROL_TRUE;
+	case TVI_CONTROL_VID_GET_BRIGHTNESS:
+	    control.id = V4L2_CID_BRIGHTNESS;
+	    if (get_control(priv, &control, 1) == TVI_CONTROL_TRUE) {
+		*(int *)arg = control.value;
+		return TVI_CONTROL_TRUE;
+	    }
+	    return TVI_CONTROL_FALSE;
+	case TVI_CONTROL_VID_SET_BRIGHTNESS:
+	    control.id = V4L2_CID_BRIGHTNESS;
+	    control.value = *(int *)arg;
+	    return set_control(priv, &control, 1);
+	case TVI_CONTROL_VID_GET_HUE:
+	    control.id = V4L2_CID_HUE;
+	    if (get_control(priv, &control, 1) == TVI_CONTROL_TRUE) {
+		*(int *)arg = control.value;
+		return TVI_CONTROL_TRUE;
+	    }
+	    return TVI_CONTROL_FALSE;
+	case TVI_CONTROL_VID_SET_HUE:
+	    control.id = V4L2_CID_HUE;
+	    control.value = *(int *)arg;
+	    return set_control(priv, &control, 1);
+	case TVI_CONTROL_VID_GET_SATURATION:
+	    control.id = V4L2_CID_SATURATION;
+	    if (get_control(priv, &control, 1) == TVI_CONTROL_TRUE) {
+		*(int *)arg = control.value;
+		return TVI_CONTROL_TRUE;
+	    }
+	    return TVI_CONTROL_FALSE;
+	case TVI_CONTROL_VID_SET_SATURATION:
+	    control.id = V4L2_CID_SATURATION;
+	    control.value = *(int *)arg;
+	    return set_control(priv, &control, 1);
+	case TVI_CONTROL_VID_GET_CONTRAST:
+	    control.id = V4L2_CID_CONTRAST;
+	    if (get_control(priv, &control, 1) == TVI_CONTROL_TRUE) {
+		*(int *)arg = control.value;
+		return TVI_CONTROL_TRUE;
+	    }
+	    return TVI_CONTROL_FALSE;
+	case TVI_CONTROL_VID_SET_CONTRAST:
+	    control.id = V4L2_CID_CONTRAST;
+	    control.value = *(int *)arg;
+	    return set_control(priv, &control, 1);
+    case TVI_CONTROL_TUN_GET_FREQ:
+	frequency.tuner = 0;
+	frequency.type  = V4L2_TUNER_ANALOG_TV;
+	if (ioctl(priv->video_fd, VIDIOC_G_FREQUENCY, &frequency) < 0) {
+	    mp_msg(MSGT_TV,MSGL_ERR,"%s: ioctl get frequency failed: %s\n",
+		   info.short_name, strerror(errno));
+	    return TVI_CONTROL_FALSE;
+	}
+	*(int *)arg = frequency.frequency;
+	return TVI_CONTROL_TRUE;
+    case TVI_CONTROL_TUN_SET_FREQ:
+#if 0
+	if (priv->input.audioset) {
+	    set_mute(priv, 1);
+	    usleep(100000); // wait to supress noise during switching
+	}
+#endif
+	frequency.tuner = 0;
+	frequency.type  = V4L2_TUNER_ANALOG_TV;
+	frequency.frequency = *(int *)arg;
+	if (ioctl(priv->video_fd, VIDIOC_S_FREQUENCY, &frequency) < 0) {
+	    mp_msg(MSGT_TV,MSGL_ERR,"%s: ioctl set frequency failed: %s\n",
+		   info.short_name, strerror(errno));
+	    return TVI_CONTROL_FALSE;
+	}
+#if 0
+	if (priv->input.audioset) {
+	    usleep(100000); // wait to supress noise during switching
+	    set_mute(priv, 0);
+	}
+#endif
+	return TVI_CONTROL_TRUE;
+    case TVI_CONTROL_TUN_GET_TUNER:
+	mp_msg(MSGT_TV, MSGL_V, "%s: get tuner\n");
+	if (ioctl(priv->video_fd, VIDIOC_G_TUNER, &priv->tuner) < 0) {
+	    mp_msg(MSGT_TV, MSGL_ERR, "%s: ioctl get tuner failed: %s\n",
+		   info.short_name, strerror(errno));
+	    return TVI_CONTROL_FALSE;
+	}
+	return TVI_CONTROL_TRUE;
+    case TVI_CONTROL_TUN_SET_TUNER:
+	mp_msg(MSGT_TV, MSGL_V, "%s: set tuner\n");
+	if (ioctl(priv->video_fd, VIDIOC_S_TUNER, &priv->tuner) < 0) {
+	    mp_msg(MSGT_TV, MSGL_ERR, "%s: ioctl set tuner failed: %s\n",
+		   info.short_name, strerror(errno));
+	    return TVI_CONTROL_FALSE;
+	}
+	return TVI_CONTROL_TRUE;
+    case TVI_CONTROL_TUN_GET_NORM:
+	*(int *)arg = priv->standard.index;
+	return TVI_CONTROL_TRUE;
+    case TVI_CONTROL_TUN_SET_NORM:
+	priv->standard.index = *(int *)arg;
+	if (ioctl(priv->video_fd, VIDIOC_ENUMSTD, &priv->standard) < 0) {
+	    mp_msg(MSGT_TV, MSGL_ERR, "%s: ioctl enum norm failed: %s\n",
+		   info.short_name, strerror(errno));
+	    return TVI_CONTROL_FALSE;
+	}
+	mp_msg(MSGT_TV, MSGL_V, "%s: set norm: %s\n", info.short_name, priv->standard.name);
+	if (ioctl(priv->video_fd, VIDIOC_S_STD, &priv->standard.id) < 0) {
+	    mp_msg(MSGT_TV, MSGL_ERR, "%s: ioctl set norm failed: %s\n",
+		   info.short_name, strerror(errno));
+	    return TVI_CONTROL_FALSE;
+	}
+	return TVI_CONTROL_TRUE;
+    case TVI_CONTROL_SPC_GET_INPUT:
+	if (ioctl(priv->video_fd, VIDIOC_G_INPUT, (int *)arg) < 0) {
+	    mp_msg(MSGT_TV, MSGL_ERR, "%s: ioctl get input failed: %s\n",
+		   info.short_name, strerror(errno));
+	    return TVI_CONTROL_FALSE;
+	}
+	return TVI_CONTROL_TRUE;
+    case TVI_CONTROL_SPC_SET_INPUT:
+	mp_msg(MSGT_TV, MSGL_V, "%s: set input: %d\n", info.short_name, *(int *)arg);
+	priv->input.index = *(int *)arg;
+	if (ioctl(priv->video_fd, VIDIOC_ENUMINPUT, &priv->input) < 0) {
+	    mp_msg(MSGT_TV, MSGL_ERR, "%s: ioctl enum input failed: %s\n",
+		   info.short_name, strerror(errno));
+	    return TVI_CONTROL_FALSE;
+	}
+	if (ioctl(priv->video_fd, VIDIOC_S_INPUT, (int *)arg) < 0) {
+	    mp_msg(MSGT_TV, MSGL_ERR, "%s: ioctl set input failed: %s\n",
+		   info.short_name, strerror(errno));
+	    return TVI_CONTROL_FALSE;
+	}
+	return TVI_CONTROL_TRUE;
+    case TVI_CONTROL_AUD_GET_FORMAT:
+	*(int *)arg = AFMT_S16_LE;
+	mp_msg(MSGT_TV, MSGL_V, "%s: get audio format: %d\n",
+	       info.short_name, *(int *)arg);
+	return TVI_CONTROL_TRUE;
+    case TVI_CONTROL_AUD_GET_SAMPLERATE:
+	*(int *)arg = priv->audio_in.samplerate;
+	mp_msg(MSGT_TV, MSGL_V, "%s: get audio samplerate: %d\n",
+	       info.short_name, *(int *)arg);
+	return TVI_CONTROL_TRUE;
+    case TVI_CONTROL_AUD_GET_SAMPLESIZE:
+	*(int *)arg = priv->audio_in.bytes_per_sample;;
+	mp_msg(MSGT_TV, MSGL_V, "%s: get audio samplesize: %d\n",
+	       info.short_name, *(int *)arg);
+	return TVI_CONTROL_TRUE;
+    case TVI_CONTROL_AUD_GET_CHANNELS:
+	*(int *)arg = priv->audio_in.channels;
+	mp_msg(MSGT_TV, MSGL_V, "%s: get audio channels: %d\n",
+	       info.short_name, *(int *)arg);
+	return TVI_CONTROL_TRUE;
+    case TVI_CONTROL_AUD_SET_SAMPLERATE:
+	mp_msg(MSGT_TV, MSGL_V, "%s: set audio samplerate: %d\n",
+	       info.short_name, *(int *)arg);
+	if (audio_in_set_samplerate(&priv->audio_in, (int)*(void **)arg) < 0) return TVI_CONTROL_FALSE;
+//	setup_audio_buffer_sizes(priv);
+	return TVI_CONTROL_TRUE;
+    }
+    mp_msg(MSGT_TV, MSGL_V, "%s: unknown control: %d\n", info.short_name, cmd);
+    return(TVI_CONTROL_UNKNOWN);
+}
+
+
+#define PRIV ((priv_t *) (tvi_handle->priv))
+
+/* handler creator - entry point ! */
+tvi_handle_t *tvi_init_v4l2(char *video_dev, char *audio_dev)
+{
+    tvi_handle_t *tvi_handle;
+
+    /* new_handle initializes priv with memset 0 */
+    tvi_handle = new_handle();
+    if (!tvi_handle) {
+	return NULL;
+    }
+    PRIV->video_fd = -1;
+
+    PRIV->video_dev = strdup(video_dev? video_dev: "/dev/video");
+    if (!PRIV->video_dev) {
+	free_handle(tvi_handle);
+	return NULL;
+    }
+
+    if (audio_dev) {
+	PRIV->audio_dev = strdup(audio_dev);
+	if (!PRIV->audio_dev) {
+	    free(PRIV->video_dev);
+	    free_handle(tvi_handle);
+	    return NULL;
+	}
+    }
+
+    return tvi_handle;
+}
+
+#undef PRIV
+
+
+static int uninit(priv_t *priv)
+{
+    int i, frames, dropped = 0;
+
+    priv->shutdown = 1;
+    pthread_join(priv->audio_grabber_thread, NULL);
+    pthread_mutex_destroy(&priv->video_buffer_mutex);
+
+    if (priv->streamon) {
+	struct v4l2_buffer buf;
+
+	/* get performance */
+	frames = 1 + (priv->curr_frame - priv->first_frame) *
+	    priv->standard.frameperiod.denominator /
+	    priv->standard.frameperiod.numerator / 1000000;
+	dropped = frames - priv->frames;
+
+	/* turn off streaming */
+	if (ioctl(priv->video_fd, VIDIOC_STREAMOFF, &(priv->map[0].buf.type)) < 0) {
+	    mp_msg(MSGT_TV, MSGL_ERR, "%s: ioctl streamoff failed: %s\n",
+		   info.short_name, strerror(errno));
+	}
+	priv->streamon = 0;
+
+	/* unqueue all remaining buffers */
+	memset(&buf,0,sizeof(buf));
+	buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	while (!ioctl(priv->video_fd, VIDIOC_DQBUF, &buf));
+    }
+
+    /* unmap all buffers */
+    for (i = 0; i < priv->mapcount; i++) {
+	if (munmap(priv->map[i].addr, priv->map[i].len) < 0) {
+	    mp_msg(MSGT_TV, MSGL_ERR, "%s: munmap capture buffer failed: %s\n",
+		   info.short_name, strerror(errno));
+	}
+    }
+
+    /* stop audio thread */
+    if (!tv_param_noaudio) {
+	pthread_join(priv->audio_grabber_thread, NULL);
+	pthread_mutex_destroy(&priv->skew_mutex);
+    }
+
+    if (priv->input.audioset) {
+	set_mute(priv, 1);
+    }
+
+    /* free memory and close device */
+    free(priv->map);		priv->map = NULL;
+    priv->mapcount = 0;
+    close(priv->video_fd);	priv->video_fd  = -1;
+    free(priv->video_dev);	priv->video_dev = NULL;
+
+    if (priv->video_ringbuffer) {
+	int i;
+	for (i = 0; i < priv->video_buffer_size_current; i++) {
+	    free(priv->video_ringbuffer[i]);
+	}
+	free(priv->video_ringbuffer);
+    }
+    if (priv->video_timebuffer)
+	free(priv->video_timebuffer);
+    if (!tv_param_noaudio) {
+	if (priv->audio_ringbuffer)
+	    free(priv->audio_ringbuffer);
+	if (priv->audio_skew_buffer)
+	    free(priv->audio_skew_buffer);
+    }
+
+    /* show some nice statistics ;-) */
+    mp_msg(MSGT_TV, MSGL_INFO,
+	   "%s: %d frames successfully processed, %d frames dropped.\n",
+	   info.short_name, priv->frames, dropped);
+    mp_msg(MSGT_TV, MSGL_V, "%s: up to %u video frames buffered.\n",
+	   info.short_name, priv->video_buffer_size_current);
+    return 1;
+}
+
+
+/* initialisation */
+static int init(priv_t *priv)
+{
+    int i;
+
+    if (tv_param_immediate == 1)
+	tv_param_noaudio = 1;
+
+    priv->audio_ringbuffer = NULL;
+    priv->audio_skew_buffer = NULL;
+
+    /* Open the video device. */
+    priv->video_fd = open(priv->video_dev, O_RDWR);
+    if (priv->video_fd < 0) {
+	mp_msg(MSGT_TV, MSGL_ERR, "%s: unable to open '%s': %s\n",
+	       info.short_name, priv->video_dev, strerror(errno));
+	uninit(priv);
+	return 0;
+    }
+    mp_msg(MSGT_TV, MSGL_DBG2, "%s: video fd: %s: %d\n",
+	   info.short_name, priv->video_dev, priv->video_fd);
+
+    /*
+    ** Query the video capabilities and current settings
+    ** for further control calls.
+    */
+    if (ioctl(priv->video_fd, VIDIOC_QUERYCAP, &priv->capability) < 0) {
+	mp_msg(MSGT_TV, MSGL_ERR, "%s: ioctl query capabilities failed: %s\n",
+	       info.short_name, strerror(errno));
+	uninit(priv);
+	return 0;
+    }
+
+    if (!(priv->capability.capabilities & V4L2_CAP_VIDEO_CAPTURE))
+    {
+	mp_msg(MSGT_TV, MSGL_ERR, "Device %s is not a video capture device.\n",
+	       priv->video_dev);
+	return 0;
+    }
+
+    if (getfmt(priv) < 0 || getstd(priv) < 0) {
+	uninit(priv);
+	return 0;
+    }
+    /*
+    ** if this device has got a tuner query it's settings
+    ** otherwise set some nice defaults
+    */
+    if (priv->capability.capabilities & V4L2_CAP_TUNER) {
+	if (ioctl(priv->video_fd, VIDIOC_G_TUNER, &priv->tuner) < 0) {
+	    mp_msg(MSGT_TV, MSGL_ERR, "%s: ioctl get tuner failed: %s\n",
+		   info.short_name, strerror(errno));
+	    uninit(priv);
+	    return 0;
+	}
+    }
+    mp_msg(MSGT_TV, MSGL_INFO, "Selected device: %s\n", priv->capability.card);
+    if (priv->capability.capabilities & V4L2_CAP_TUNER) {
+	mp_msg(MSGT_TV, MSGL_INFO, " Tuner cap:%s%s%s\n",
+		(priv->tuner.capability & V4L2_TUNER_CAP_STEREO) ? " STEREO" : "",
+		(priv->tuner.capability & V4L2_TUNER_CAP_LANG1)  ? " LANG1"  : "",
+		(priv->tuner.capability & V4L2_TUNER_CAP_LANG2)  ? " LANG2"  : "");
+	mp_msg(MSGT_TV, MSGL_INFO, " Tuner rxs:%s%s%s%s\n",
+		(priv->tuner.rxsubchans & V4L2_TUNER_SUB_MONO)   ? " MONO"   : "",
+		(priv->tuner.rxsubchans & V4L2_TUNER_SUB_STEREO) ? " STEREO" : "",
+		(priv->tuner.rxsubchans & V4L2_TUNER_SUB_LANG1)  ? " LANG1"  : "",
+		(priv->tuner.rxsubchans & V4L2_TUNER_SUB_LANG2)  ? " LANG2"  : "");
+    }
+    mp_msg(MSGT_TV, MSGL_INFO, " Capabilites:%s%s%s%s%s%s%s%s%s%s%s\n",
+	   priv->capability.capabilities & V4L2_CAP_VIDEO_CAPTURE?
+	   "  video capture": "",
+	   priv->capability.capabilities & V4L2_CAP_VIDEO_OUTPUT?
+	   "  video output": "",
+	   priv->capability.capabilities & V4L2_CAP_VIDEO_OVERLAY?
+	   "  video overlay": "",
+	   priv->capability.capabilities & V4L2_CAP_VBI_CAPTURE?
+	   "  VBI capture device": "",
+	   priv->capability.capabilities & V4L2_CAP_VBI_OUTPUT?
+	   "  VBI output": "",
+	   priv->capability.capabilities & V4L2_CAP_RDS_CAPTURE?
+	   "  RDS data capture": "",
+	   priv->capability.capabilities & V4L2_CAP_TUNER?
+	   "  tuner": "",
+	   priv->capability.capabilities & V4L2_CAP_AUDIO?
+	   "  audio": "",
+	   priv->capability.capabilities & V4L2_CAP_READWRITE?
+	   "  read/write": "",
+	   priv->capability.capabilities & V4L2_CAP_ASYNCIO?
+	   "  async i/o": "",
+	   priv->capability.capabilities & V4L2_CAP_STREAMING?
+	   "  streaming": "");
+    mp_msg(MSGT_TV, MSGL_INFO, " supported norms:");
+    for (i = 0;; i++) {
+	struct v4l2_standard standard;
+	memset(&standard, 0, sizeof(standard));
+	standard.index = i;
+	if (-1 == ioctl(priv->video_fd, VIDIOC_ENUMSTD, &standard))
+	    break;
+	printf(" %d = %s;", i, standard.name);
+    }
+    mp_msg(MSGT_TV, MSGL_INFO, "\n inputs:");
+    for (i = 0; 1; i++) {
+	struct v4l2_input input;
+
+	input.index = i;
+	if (ioctl(priv->video_fd, VIDIOC_ENUMINPUT, &input) < 0) {
+	    break;
+	}
+	mp_msg(MSGT_TV, MSGL_INFO, " %d = %s;", i, input.name);
+    }
+    if (ioctl(priv->video_fd, VIDIOC_G_INPUT, &i) < 0) {
+	mp_msg(MSGT_TV, MSGL_ERR, "%s: ioctl get input failed: %s\n",
+	       info.short_name, strerror(errno));
+    }
+    mp_msg(MSGT_TV, MSGL_INFO, "\n Current input: %d\n", i);
+    for (i = 0; ; i++) {
+	struct v4l2_fmtdesc fmtdesc;
+
+	fmtdesc.index = i;
+	fmtdesc.type  = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	if (ioctl(priv->video_fd, VIDIOC_ENUM_FMT, &fmtdesc) < 0) {
+	    break;
+	}
+	mp_msg(MSGT_TV, MSGL_V, " Format %-6s (%2d bits, %s): %s\n",
+	       pixfmt2name(fmtdesc.pixelformat), pixfmt2depth(fmtdesc.pixelformat),
+	       fmtdesc.description, vo_format_name(fcc_vl2mp(fmtdesc.pixelformat)));
+    }
+    mp_msg(MSGT_TV, MSGL_INFO, " Current format: %s\n",
+	   pixfmt2name(priv->format.fmt.pix.pixelformat));
+
+    /* set some nice defaults */
+    if (getfmt(priv) < 0) return 0;
+    priv->format.fmt.pix.width  = 640;
+    priv->format.fmt.pix.height = 480;
+    if (ioctl(priv->video_fd, VIDIOC_S_FMT, &priv->format) < 0) {
+	mp_msg(MSGT_TV, MSGL_ERR, "%s: ioctl set format failed: %s\n",
+	       info.short_name, strerror(errno));
+	uninit(priv);
+	return 0;
+    }
+
+//    if (!(priv->capability.capabilities & V4L2_CAP_AUDIO) && !tv_param_force_audio) tv_param_noaudio = 1;
+
+    if (priv->capability.capabilities & V4L2_CAP_TUNER) {
+	struct v4l2_control control;
+	if (tv_param_amode >= 0) {
+	    mp_msg(MSGT_TV, MSGL_V, "%s: setting audio mode\n", info.short_name);
+	    priv->tuner.audmode = amode2v4l(tv_param_amode);
+	    if (ioctl(priv->video_fd, VIDIOC_S_TUNER, &priv->tuner) < 0) {
+		mp_msg(MSGT_TV, MSGL_ERR, "%s: ioctl set tuner failed: %s\n",
+		       info.short_name, strerror(errno));
+		return TVI_CONTROL_FALSE;
+	    }
+	}
+	mp_msg(MSGT_TV, MSGL_INFO, "%s: current audio mode is :%s%s%s%s\n", info.short_name,
+		(priv->tuner.audmode == V4L2_TUNER_MODE_MONO)   ? " MONO"   : "",
+		(priv->tuner.audmode == V4L2_TUNER_MODE_STEREO) ? " STEREO" : "",
+		(priv->tuner.audmode == V4L2_TUNER_MODE_LANG1)  ? " LANG1"  : "",
+		(priv->tuner.audmode == V4L2_TUNER_MODE_LANG2)  ? " LANG2"  : "");
+
+	if (tv_param_volume >= 0) {
+	    control.id = V4L2_CID_AUDIO_VOLUME;
+	    control.value = tv_param_volume;
+	    set_control(priv, &control, 0);
+	}
+	if (tv_param_bass >= 0) {
+	    control.id = V4L2_CID_AUDIO_BASS;
+	    control.value = tv_param_bass;
+	    set_control(priv, &control, 0);
+	}
+	if (tv_param_treble >= 0) {
+	    control.id = V4L2_CID_AUDIO_TREBLE;
+	    control.value = tv_param_treble;
+	    set_control(priv, &control, 0);
+	}
+	if (tv_param_balance >= 0) {
+	    control.id = V4L2_CID_AUDIO_BALANCE;
+	    control.value = tv_param_balance;
+	    set_control(priv, &control, 0);
+	}
+    }
+    
+    /* audio init */
+    if (!tv_param_noaudio) {
+#ifdef HAVE_ALSA9
+	if (tv_param_alsa)
+	    audio_in_init(&priv->audio_in, AUDIO_IN_ALSA);
+	else
+	    audio_in_init(&priv->audio_in, AUDIO_IN_OSS);
+#else
+	audio_in_init(&priv->audio_in, AUDIO_IN_OSS);
+#endif
+
+	if (priv->audio_dev) {
+	    audio_in_set_device(&priv->audio_in, priv->audio_dev);
+	}
+
+	audio_in_set_samplerate(&priv->audio_in, 44100);
+	if (priv->capability.capabilities & V4L2_CAP_TUNER) {
+	    if (priv->tuner.audmode == V4L2_TUNER_MODE_STEREO) {
+		audio_in_set_channels(&priv->audio_in, 2);
+	    } else {
+		audio_in_set_channels(&priv->audio_in, 1);
+	    }
+	} else {
+	    if (tv_param_forcechan >= 0) {
+		audio_in_set_channels(&priv->audio_in, tv_param_forcechan);
+	    } else {
+		audio_in_set_channels(&priv->audio_in, 2);
+	    }
+	}
+	if (audio_in_setup(&priv->audio_in) < 0) return 0;
+//	setup_audio_buffer_sizes(priv);
+    }
+
+    return 1;
+}
+
+static int get_capture_buffer_size(priv_t *priv)
+{
+    int bufsize, cnt;
+    int w = priv->format.fmt.pix.width;
+    int h = priv->format.fmt.pix.height;
+    int d = pixfmt2depth(priv->format.fmt.pix.pixelformat);
+    int bytesperline = w*d/8;
+
+    if (tv_param_buffer_size >= 0) {
+	bufsize = tv_param_buffer_size*1024*1024;
+    } else {
+#ifdef HAVE_SYS_SYSINFO_H
+	struct sysinfo si;
+	
+	sysinfo(&si);
+	if (si.totalram<2*1024*1024) {
+	    bufsize = 1024*1024;
+	} else {
+	    bufsize = si.totalram/2;
+	}
+#else
+	bufsize = 16*1024*1024;
+#endif
+    }
+    
+    cnt = bufsize/(h*bytesperline);
+    if (cnt < 2) cnt = 2;
+    
+    return cnt;
+}
+
+/* that's the real start, we'got the format parameters (checked with control) */
+static int start(priv_t *priv)
+{
+    struct v4l2_requestbuffers request;
+    int i;
+
+    /* setup audio parameters */
+
+    /* we need this to size the audio buffer properly */
+    if (priv->immediate_mode) {
+	priv->video_buffer_size_max = 2;
+    } else {
+	priv->video_buffer_size_max = get_capture_buffer_size(priv);
+    }
+    
+    if (!tv_param_noaudio) {
+	setup_audio_buffer_sizes(priv);
+	priv->audio_skew_buffer = (long long*)malloc(sizeof(long long)*priv->aud_skew_cnt);
+	if (!priv->audio_skew_buffer) {
+	    mp_msg(MSGT_TV, MSGL_ERR, "cannot allocate skew buffer: %s\n", strerror(errno));
+	    return 0;
+	}
+
+	priv->audio_ringbuffer = (unsigned char*)malloc(priv->audio_in.blocksize*priv->audio_buffer_size);
+	if (!priv->audio_ringbuffer) {
+	    mp_msg(MSGT_TV, MSGL_ERR, "cannot allocate audio buffer: %s\n", strerror(errno));
+	    return 0;
+	}
+
+	priv->audio_secs_per_block = (double)priv->audio_in.blocksize/(priv->audio_in.samplerate
+								    *priv->audio_in.channels
+								    *priv->audio_in.bytes_per_sample);
+	priv->audio_head = 0;
+	priv->audio_tail = 0;
+	priv->audio_cnt = 0;
+	priv->audio_drop = 0;
+	priv->audio_skew = 0;
+	priv->audio_skew_total = 0;
+	priv->audio_recv_blocks_total = 0;
+	priv->audio_sent_blocks_total = 0;
+    }
+
+    /* setup video parameters */
+    if (!tv_param_noaudio) {
+	if (priv->video_buffer_size_max < 3.0*(priv->standard.frameperiod.denominator /
+					       priv->standard.frameperiod.numerator)
+	    *priv->audio_secs_per_block) {
+	    mp_msg(MSGT_TV, MSGL_ERR, "Video buffer shorter than 3 times audio frame duration.\n"
+		   "You will probably experience heavy framedrops.\n");
+	}
+    }
+    
+    {
+	int bytesperline = priv->format.fmt.pix.width*pixfmt2depth(priv->format.fmt.pix.pixelformat)/8;
+	
+	mp_msg(MSGT_TV, MSGL_V, "Using a ring buffer for maximum %d frames, %d MB total size.\n",
+	       priv->video_buffer_size_max,
+	       priv->video_buffer_size_max*priv->format.fmt.pix.height*bytesperline/(1024*1024));
+    }
+
+    priv->video_ringbuffer = (unsigned char**)malloc(priv->video_buffer_size_max*sizeof(unsigned char*));
+    if (!priv->video_ringbuffer) {
+	mp_msg(MSGT_TV, MSGL_ERR, "cannot allocate video buffer: %s\n", strerror(errno));
+	return 0;
+    }
+    for (i = 0; i < priv->video_buffer_size_max; i++)
+	priv->video_ringbuffer[i] = NULL;
+    priv->video_timebuffer = (long long*)malloc(sizeof(long long) * priv->video_buffer_size_max);
+    if (!priv->video_timebuffer) {
+	mp_msg(MSGT_TV, MSGL_ERR, "cannot allocate time buffer: %s\n", strerror(errno));
+	return 0;
+    }
+
+    priv->video_head = 0;
+    priv->video_tail = 0;
+    priv->video_cnt = 0;
+    
+    /* request buffers */
+    if (priv->immediate_mode) {
+	request.count = 2;
+    } else {
+	request.count = BUFFER_COUNT;
+    }
+    
+    request.type  = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+    request.memory = V4L2_MEMORY_MMAP;
+    if (ioctl(priv->video_fd, VIDIOC_REQBUFS, &request) < 0) {
+	mp_msg(MSGT_TV, MSGL_ERR, "%s: ioctl request buffers failed: %s\n",
+	       info.short_name, strerror(errno));
+	return 0;
+    }
+
+    /* query buffers */
+    if (!(priv->map = malloc(sizeof(struct map) * request.count))) {
+	mp_msg(MSGT_TV, MSGL_ERR, "%s: malloc capture buffers failed: %s\n",
+	       info.short_name, strerror(errno));
+	return 0;
+    }
+
+    /* map and queue buffers */
+    for (i = 0; i < request.count; i++) {
+	memset(&priv->map[i].buf,0,sizeof(priv->map[i].buf));
+	priv->map[i].buf.index = i;
+	priv->map[i].buf.type  = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	priv->map[i].buf.memory  = V4L2_MEMORY_MMAP;
+	if (ioctl(priv->video_fd, VIDIOC_QUERYBUF, &(priv->map[i].buf)) < 0) {
+	    mp_msg(MSGT_TV, MSGL_ERR, "%s: ioctl query buffer failed: %s\n",
+		   info.short_name, strerror(errno));
+	    free(priv->map);
+	    priv->map = NULL;
+	    return 0;
+	}
+	priv->map[i].addr = mmap (0, priv->map[i].buf.length, PROT_READ |
+				  PROT_WRITE, MAP_SHARED, priv->video_fd, priv->map[i].buf.m.offset);
+	if (priv->map[i].addr == MAP_FAILED) {
+	    mp_msg(MSGT_TV, MSGL_ERR, "%s: mmap capture buffer failed: %s\n",
+		   info.short_name, strerror(errno));
+	    priv->map[i].len = 0;
+	    return 0;
+	}
+	priv->map[i].len = priv->map[i].buf.length;
+	/* count up to make sure this is correct everytime */
+	priv->mapcount++;
+
+	if (ioctl(priv->video_fd, VIDIOC_QBUF, &(priv->map[i].buf)) < 0) {
+	    mp_msg(MSGT_TV, MSGL_ERR, "%s: ioctl queue buffer failed: %s\n",
+		   info.short_name, strerror(errno));
+	    return 0;
+	}
+    }
+
+    /* start audio thread */
+    priv->shutdown = 0;
+    priv->audio_skew_measure_time = 0;
+    priv->first_frame = 0;
+    priv->audio_skew = 0;
+    priv->first = 1;
+
+    if (priv->input.audioset) {
+	set_mute(priv, 0);
+    }
+    
+    return 1;
+}
+
+
+#ifdef HAVE_TV_BSDBT848
+static double grabimmediate_video_frame(priv_t *priv, char *buffer, int len)
+{
+    memset(buffer, 0xCC, len);
+    return(1);
+}
+#endif /* HAVE_TV_BSDBT848 */
+
+// copies a video frame
+// for YV12 swaps the 2nd and 3rd plane
+static inline void copy_frame(priv_t *priv, unsigned char *dest, unsigned char *source)
+{
+    int w = priv->format.fmt.pix.width;
+    int h = priv->format.fmt.pix.height;
+    int d = pixfmt2depth(priv->format.fmt.pix.pixelformat);
+    int bytesperline = w*d/8;
+
+    // YV12 uses VIDEO_PALETTE_YUV420P, but the planes are swapped
+    switch (priv->mp_format) {
+    case IMGFMT_YV12:
+	memcpy(dest, source, w * h);
+	memcpy(dest+w * h*5/4, source+w * h, w * h/4);
+	memcpy(dest+w * h, source+w * h*5/4, w * h/4);
+	break;
+    default:
+	memcpy(dest, source, bytesperline * h);
+    }
+    
+}
+
+// maximum skew change, in frames
+#define MAX_SKEW_DELTA 0.6
+static void *video_grabber(void *data)
+{
+    priv_t *priv = (priv_t*)data;
+    long long skew, prev_skew, xskew, interval, prev_interval;
+    int i;
+    int framesize = priv->format.fmt.pix.height*priv->format.fmt.pix.width*
+	pixfmt2depth(priv->format.fmt.pix.pixelformat)/8;
+    fd_set rdset;
+    struct timeval timeout;
+    struct v4l2_buffer buf;
+
+    xskew = 0;
+    skew = 0;
+    interval = 0;
+    prev_interval = 0;
+    prev_skew = 0;
+
+    mp_msg(MSGT_TV, MSGL_V, "%s: going to capture\n", info.short_name);
+    if (ioctl(priv->video_fd, VIDIOC_STREAMON, &(priv->format.type)) < 0) {
+	mp_msg(MSGT_TV, MSGL_ERR, "%s: ioctl streamon failed: %s\n",
+	       info.short_name, strerror(errno));
+	return 0;
+    }
+    priv->streamon = 1;
+
+    if (!tv_param_noaudio) {
+	pthread_mutex_init(&priv->skew_mutex, NULL);
+	pthread_create(&priv->audio_grabber_thread, NULL, audio_grabber, priv);
+    }
+
+    for (priv->frames = 0; !priv->shutdown;)
+    {
+	int ret;
+	
+	if (priv->immediate_mode) {
+	    while (priv->video_cnt == priv->video_buffer_size_max) {
+		usleep(10000);
+		if (priv->shutdown) {
+		    return NULL;
+		}
+	    }
+	}
+		
+	FD_ZERO (&rdset);
+	FD_SET (priv->video_fd, &rdset);
+
+	timeout.tv_sec = 1;
+	timeout.tv_usec = 0;
+
+	i = select(priv->video_fd + 1, &rdset, NULL, NULL, &timeout);
+	if (i < 0) {
+	    mp_msg(MSGT_TV, MSGL_ERR, "%s: select failed: %s\n",
+		   info.short_name, strerror(errno));
+	    continue;
+	}
+	else if (i == 0) {
+	    mp_msg(MSGT_TV, MSGL_ERR, "%s: select timeout\n", info.short_name);
+	    continue;
+	}
+	else if (!FD_ISSET(priv->video_fd, &rdset)) {
+	    continue;
+	}
+
+	memset(&buf,0,sizeof(buf));
+	buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	ret = ioctl(priv->video_fd, VIDIOC_DQBUF, &buf);
+
+	if (ret < 0) {
+	    /*
+	      if there's no signal, the buffer might me dequeued
+	      so we query all the buffers to see which one we should
+	      put back to queue
+
+	      observed with saa7134 0.2.8
+	      don't know if is it a bug or (mis)feature
+	     */
+	    mp_msg(MSGT_TV, MSGL_ERR, "%s: ioctl dequeue buffer failed: %s, idx = %d\n",
+		   info.short_name, strerror(errno), buf.index);
+	    for (i = 0; i < priv->mapcount; i++) {
+		memset(&buf,0,sizeof(buf));
+		buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+		buf.index = i;
+		ret = ioctl(priv->video_fd, VIDIOC_QUERYBUF, &buf);
+		if (ret < 0) {
+		    mp_msg(MSGT_TV, MSGL_ERR, "%s: ioctl query buffer failed: %s, idx = %d\n",
+			   info.short_name, strerror(errno), buf.index);
+		    return 0;
+		}
+		if ((buf.flags & (V4L2_BUF_FLAG_QUEUED | V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_DONE)) == V4L2_BUF_FLAG_MAPPED) {
+		    if (ioctl(priv->video_fd, VIDIOC_QBUF, &(priv->map[i].buf)) < 0) {
+			mp_msg(MSGT_TV, MSGL_ERR, "%s: ioctl queue buffer failed: %s\n",
+			       info.short_name, strerror(errno));
+			return 0;
+		    }		
+		}
+	    }
+	    continue;
+	}
+
+	/* store the timestamp of the very first frame as reference */
+	if (!priv->frames++) {
+	    priv->first_frame = (long long)1e6*buf.timestamp.tv_sec + buf.timestamp.tv_usec;
+	}
+	priv->curr_frame = (long long)buf.timestamp.tv_sec*1e6+buf.timestamp.tv_usec;
+//	fprintf(stderr, "idx = %d, ts = %lf\n", buf.index, (double)(priv->curr_frame) / 1e6);
+
+	interval = priv->curr_frame - priv->first_frame;
+
+	if (!priv->immediate_mode) {
+	    // interpolate the skew in time
+	    pthread_mutex_lock(&priv->skew_mutex);
+	    xskew = priv->audio_skew + (interval - priv->audio_skew_measure_time)*priv->audio_skew_factor;
+	    pthread_mutex_unlock(&priv->skew_mutex);
+ 	    // correct extreme skew changes to avoid (especially) moving backwards in time
+	    if (xskew - prev_skew > (interval - prev_interval)*MAX_SKEW_DELTA) {
+		skew = prev_skew + (interval - prev_interval)*MAX_SKEW_DELTA;
+	    } else if (xskew - prev_skew < -(interval - prev_interval)*MAX_SKEW_DELTA) {
+		skew = prev_skew - (interval - prev_interval)*MAX_SKEW_DELTA;
+	    } else {
+		skew = xskew;
+	    }
+	}
+
+	mp_msg(MSGT_TV, MSGL_DBG3, "\nfps = %lf, interval = %lf, a_skew = %f, corr_skew = %f\n",
+	       (interval != prev_interval) ? (double)1e6/(interval - prev_interval) : -1,
+	       (double)1e-6*interval, (double)1e-6*xskew, (double)1e-6*skew);
+	mp_msg(MSGT_TV, MSGL_DBG3, "vcnt = %d, acnt = %d\n", priv->video_cnt, priv->audio_cnt);
+
+	prev_skew = skew;
+	prev_interval = interval;
+
+	/* allocate a new buffer, if needed */
+	pthread_mutex_lock(&priv->video_buffer_mutex);
+	if (priv->video_buffer_size_current < priv->video_buffer_size_max) {
+	    if (priv->video_cnt == priv->video_buffer_size_current) {
+		unsigned char *newbuf = (unsigned char*)malloc(framesize);
+		if (newbuf) {
+		    memmove(priv->video_ringbuffer+priv->video_tail+1, priv->video_ringbuffer+priv->video_tail,
+			    (priv->video_buffer_size_current-priv->video_tail)*sizeof(unsigned char *));
+		    memmove(priv->video_timebuffer+priv->video_tail+1, priv->video_timebuffer+priv->video_tail,
+			    (priv->video_buffer_size_current-priv->video_tail)*sizeof(long long));
+		    priv->video_ringbuffer[priv->video_tail] = newbuf;
+		    if ((priv->video_head >= priv->video_tail) && (priv->video_cnt > 0)) priv->video_head++;
+		    priv->video_buffer_size_current++;
+		}
+	    }
+	}
+	pthread_mutex_unlock(&priv->video_buffer_mutex);
+
+	if (priv->video_cnt == priv->video_buffer_size_current) {
+	    if (!priv->immediate_mode) {
+		mp_msg(MSGT_TV, MSGL_ERR, "\nvideo buffer full - dropping frame\n");
+	    }
+	} else {
+	    if (priv->immediate_mode) {
+		priv->video_timebuffer[priv->video_tail] = 0;
+	    } else {
+		// compensate for audio skew
+		// negative skew => there are more audio samples, increase interval
+		// positive skew => less samples, shorten the interval
+		priv->video_timebuffer[priv->video_tail] = interval - skew;
+	    }
+		
+	    copy_frame(priv, priv->video_ringbuffer[priv->video_tail], priv->map[buf.index].addr);
+	    priv->video_tail = (priv->video_tail+1)%priv->video_buffer_size_current;
+	    priv->video_cnt++;
+	}
+	if (ioctl(priv->video_fd, VIDIOC_QBUF, &buf) < 0) {
+	    mp_msg(MSGT_TV, MSGL_ERR, "%s: ioctl queue buffer failed: %s\n",
+		   info.short_name, strerror(errno));
+	    return 0;
+	}
+    }
+    return NULL;
+}
+
+static double grab_video_frame(priv_t *priv, char *buffer, int len)
+{
+    double interval;
+
+    if (priv->first) {
+	pthread_create(&priv->video_grabber_thread, NULL, video_grabber, priv);
+	priv->first = 0;
+    }
+
+    while (priv->video_cnt == 0) {
+	usleep(10000);
+    }
+
+    pthread_mutex_lock(&priv->video_buffer_mutex);
+    interval = (double)priv->video_timebuffer[priv->video_head]*1e-6;
+    memcpy(buffer, priv->video_ringbuffer[priv->video_head], len);
+    priv->video_cnt--;
+    priv->video_head = (priv->video_head+1)%priv->video_buffer_size_current;
+    pthread_mutex_unlock(&priv->video_buffer_mutex);
+
+    return interval;
+}
+
+static int get_video_framesize(priv_t *priv)
+{
+    return priv->format.fmt.pix.sizeimage;
+}
+
+/*
+// for testing purposes only
+static void read_doublespeed(priv_t *priv)
+{
+    char *bufx = (char*)malloc(priv->audio_in.blocksize*2);
+    short *s;
+    short *d;
+    int i;
+    
+    audio_in_read_chunk(&priv->audio_in, bufx);
+    audio_in_read_chunk(&priv->audio_in, bufx+priv->audio_in.blocksize);
+
+    s = bufx;
+    d = priv->audio_ringbuffer+priv->audio_tail*priv->audio_in.blocksize;
+    for (i = 0; i < priv->audio_in.blocksize/2; i++) {
+	*d++ = *s++;
+	*s++;
+    }
+    
+}
+*/
+
+static void *audio_grabber(void *data)
+{
+    priv_t *priv = (priv_t*)data;
+    struct timeval tv;
+    int i, audio_skew_ptr = 0;
+    long long current_time, prev_skew = 0;
+
+    gettimeofday(&tv, NULL);
+    priv->audio_start_time = (long long)1e6*tv.tv_sec + tv.tv_usec;
+    audio_in_start_capture(&priv->audio_in);
+    for (i = 0; i < priv->aud_skew_cnt; i++)
+	priv->audio_skew_buffer[i] = 0;
+
+    for (; !priv->shutdown;)
+    {
+//	read_doublespeed(priv);
+	if (audio_in_read_chunk(&priv->audio_in, priv->audio_ringbuffer+priv->audio_tail*priv->audio_in.blocksize) < 0)
+	    continue;
+
+	pthread_mutex_lock(&priv->skew_mutex);
+	if (priv->first_frame == 0) {
+	    // there is no first frame yet (unlikely to happen)
+//	    fprintf(stderr, "warning - first frame not yet available!\n");
+	    pthread_mutex_unlock(&priv->skew_mutex);
+	    continue;
+	}
+	pthread_mutex_unlock(&priv->skew_mutex);
+
+	gettimeofday(&tv, NULL);
+
+	priv->audio_recv_blocks_total++;
+	current_time = (long long)1e6*tv.tv_sec + tv.tv_usec - priv->audio_start_time;
+
+//	fprintf(stderr, "spb = %lf, bs = %d, skew = %lf\n", priv->audio_secs_per_block, priv->audio_in.blocksize,
+//		(double)(current_time - 1e6*priv->audio_secs_per_block*priv->audio_recv_blocks_total)/1e6);
+
+	priv->audio_skew_total -= priv->audio_skew_buffer[audio_skew_ptr];
+	priv->audio_skew_buffer[audio_skew_ptr] = current_time
+	    - 1e6*priv->audio_secs_per_block*priv->audio_recv_blocks_total;
+	priv->audio_skew_total += priv->audio_skew_buffer[audio_skew_ptr];
+	audio_skew_ptr = (audio_skew_ptr+1) % priv->aud_skew_cnt;
+
+	pthread_mutex_lock(&priv->skew_mutex);
+	// linear interpolation - here we interpolate current skew value
+	// from the moving average, which we expect to be in the middle
+	// of the interval
+	if (priv->audio_recv_blocks_total > priv->aud_skew_cnt) {
+	    priv->audio_skew = priv->audio_skew_total/priv->aud_skew_cnt;
+	    priv->audio_skew += (priv->audio_skew*priv->aud_skew_cnt)/(2*priv->audio_recv_blocks_total-priv->aud_skew_cnt);
+	} else {
+	    // this smoothens the evolution of audio_skew at startup a bit
+	    priv->audio_skew = ((priv->aud_skew_cnt+priv->audio_recv_blocks_total)*priv->audio_skew_total)/(priv->aud_skew_cnt*priv->audio_recv_blocks_total);
+	}
+//	fprintf(stderr, "audio_skew = %lf\n", (double)priv->audio_skew/1e6);
+	// current skew factor (assuming linearity)
+	// used for further interpolation in video_grabber
+	// probably overkill but seems to be necessary for
+	// stress testing by dropping half of the audio frames ;)
+	// especially when using ALSA with large block sizes
+	// where audio_skew remains a long while behind
+	if ((priv->audio_skew_measure_time != 0) && (current_time - priv->audio_skew_measure_time != 0)) {
+	    priv->audio_skew_factor = (double)(priv->audio_skew-prev_skew)/(current_time - priv->audio_skew_measure_time);
+	} else {
+	    priv->audio_skew_factor = 0.0;
+	}
+	
+	priv->audio_skew_measure_time = current_time;
+	prev_skew = priv->audio_skew;
+	priv->audio_skew -= priv->audio_start_time - priv->first_frame;
+	pthread_mutex_unlock(&priv->skew_mutex);
+	
+	if ((priv->audio_tail+1) % priv->audio_buffer_size == priv->audio_head) {
+	    mp_msg(MSGT_TV, MSGL_ERR, "\ntoo bad - dropping audio frame !\n");
+	    priv->audio_drop++;
+	} else {
+	    priv->audio_tail = (priv->audio_tail+1) % priv->audio_buffer_size;
+	    priv->audio_cnt++;
+	}
+    }
+    return NULL;
+}
+
+static double grab_audio_frame(priv_t *priv, char *buffer, int len)
+{
+    mp_dbg(MSGT_TV, MSGL_DBG2, "grab_audio_frame(priv=%p, buffer=%p, len=%d)\n",
+	priv, buffer, len);
+
+    // compensate for dropped audio frames
+    if (priv->audio_drop && (priv->audio_head == priv->audio_tail)) {
+	priv->audio_drop--;
+	priv->audio_sent_blocks_total++;
+	memset(buffer, 0, len);
+	return (double)priv->audio_sent_blocks_total*priv->audio_secs_per_block;
+    }
+
+    while (priv->audio_head == priv->audio_tail) {
+	usleep(10000);
+    }
+    memcpy(buffer, priv->audio_ringbuffer+priv->audio_head*priv->audio_in.blocksize, len);
+    priv->audio_head = (priv->audio_head+1) % priv->audio_buffer_size;
+    priv->audio_cnt--;
+    priv->audio_sent_blocks_total++;
+    return (double)priv->audio_sent_blocks_total*priv->audio_secs_per_block;
+}
+
+static int get_audio_framesize(priv_t *priv)
+{
+    return(priv->audio_in.blocksize);
+}
+
+#endif /* USE_TV || HAVE_TV_V4L2 */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmpdemux/videodev2.h	Thu Aug 07 12:18:04 2003 +0000
@@ -0,0 +1,862 @@
+#ifndef __LINUX_VIDEODEV2_H
+#define __LINUX_VIDEODEV2_H
+/*
+ *	Video for Linux Two
+ *
+ *	Header file for v4l or V4L2 drivers and applications, for
+ *	Linux kernels 2.2.x or 2.4.x.
+ *
+ *	See http://www.thedirks.org/v4l2/ for API specs and other
+ *	v4l2 documentation.
+ *
+ *	Author: Bill Dirks <bdirks@pacbell.net>
+ *		Justin Schoeman
+ *		et al.
+ */
+#include <sys/time.h> /* need struct timeval */
+
+#include <linux/types.h>
+#include <linux/version.h>
+
+/*
+ *	M I S C E L L A N E O U S
+ */
+
+/*  Four-character-code (FOURCC) */
+#define v4l2_fourcc(a,b,c,d)\
+        (((__u32)(a)<<0)|((__u32)(b)<<8)|((__u32)(c)<<16)|((__u32)(d)<<24))
+
+/*
+ *	E N U M S
+ */
+enum v4l2_field {
+	V4L2_FIELD_ANY        = 0, /* driver can choose from none,
+				      top, bottom, interlaced
+				      depending on whatever it thinks
+				      is approximate ... */
+	V4L2_FIELD_NONE       = 1, /* this device has no fields ... */
+	V4L2_FIELD_TOP        = 2, /* top field only */
+	V4L2_FIELD_BOTTOM     = 3, /* bottom field only */
+	V4L2_FIELD_INTERLACED = 4, /* both fields interlaced */
+	V4L2_FIELD_SEQ_TB     = 5, /* both fields sequential into one
+				      buffer, top-bottom order */
+	V4L2_FIELD_SEQ_BT     = 6, /* same as above + bottom-top order */
+	V4L2_FIELD_ALTERNATE  = 7, /* both fields alternating into
+				      separate buffers */
+};
+#define V4L2_FIELD_HAS_TOP(field)	\
+	((field) == V4L2_FIELD_TOP 	||\
+	 (field) == V4L2_FIELD_INTERLACED ||\
+	 (field) == V4L2_FIELD_SEQ_TB	||\
+	 (field) == V4L2_FIELD_SEQ_BT)
+#define V4L2_FIELD_HAS_BOTTOM(field)	\
+	((field) == V4L2_FIELD_BOTTOM 	||\
+	 (field) == V4L2_FIELD_INTERLACED ||\
+	 (field) == V4L2_FIELD_SEQ_TB	||\
+	 (field) == V4L2_FIELD_SEQ_BT)
+#define V4L2_FIELD_HAS_BOTH(field)	\
+	((field) == V4L2_FIELD_INTERLACED ||\
+	 (field) == V4L2_FIELD_SEQ_TB	||\
+	 (field) == V4L2_FIELD_SEQ_BT)
+
+enum v4l2_buf_type {
+	V4L2_BUF_TYPE_VIDEO_CAPTURE  = 1,
+	V4L2_BUF_TYPE_VIDEO_OUTPUT   = 2,
+	V4L2_BUF_TYPE_VIDEO_OVERLAY  = 3,
+	V4L2_BUF_TYPE_VBI_CAPTURE    = 4,
+	V4L2_BUF_TYPE_VBI_OUTPUT     = 5,
+	V4L2_BUF_TYPE_PRIVATE        = 0x80,
+};
+
+enum v4l2_ctrl_type {
+	V4L2_CTRL_TYPE_INTEGER	     = 1,
+	V4L2_CTRL_TYPE_BOOLEAN	     = 2,
+	V4L2_CTRL_TYPE_MENU	     = 3,
+	V4L2_CTRL_TYPE_BUTTON	     = 4,
+};
+
+enum v4l2_tuner_type {
+	V4L2_TUNER_RADIO	     = 1,
+	V4L2_TUNER_ANALOG_TV	     = 2,
+};
+
+enum v4l2_memory {
+	V4L2_MEMORY_MMAP             = 1,
+	V4L2_MEMORY_USERPTR          = 2,
+	V4L2_MEMORY_OVERLAY          = 3,
+};
+
+/* see also http://vektor.theorem.ca/graphics/ycbcr/ */
+enum v4l2_colorspace {
+	/* ITU-R 601 -- broadcast NTSC/PAL */
+	V4L2_COLORSPACE_SMPTE170M     = 1,
+
+	/* 1125-Line (US) HDTV */
+	V4L2_COLORSPACE_SMPTE240M     = 2,
+
+	/* HD and modern captures. */
+	V4L2_COLORSPACE_REC709        = 3,
+	
+	/* broken BT878 extents (601, luma range 16-253 instead of 16-235) */
+	V4L2_COLORSPACE_BT878         = 4,
+	
+	/* These should be useful.  Assume 601 extents. */
+	V4L2_COLORSPACE_470_SYSTEM_M  = 5,
+	V4L2_COLORSPACE_470_SYSTEM_BG = 6,
+	
+	/* I know there will be cameras that send this.  So, this is
+	 * unspecified chromaticities and full 0-255 on each of the
+	 * Y'CbCr components
+	 */
+	V4L2_COLORSPACE_JPEG          = 7,
+	
+	/* For RGB colourspaces, this is probably a good start. */
+	V4L2_COLORSPACE_SRGB          = 8,
+};
+
+struct v4l2_rect {
+	__s32   left;
+	__s32   top;
+	__s32   width;
+	__s32   height;
+};
+
+struct v4l2_fract {
+	__u32   numerator;
+	__u32   denominator;
+};
+
+/*
+ *	D R I V E R   C A P A B I L I T I E S
+ */
+struct v4l2_capability
+{
+	__u8	driver[16];	/* i.e. "bttv" */
+	__u8	card[32];	/* i.e. "Hauppauge WinTV" */
+	__u8	bus_info[32];	/* "PCI:" + pci_dev->slot_name */
+	__u32   version;        /* should use KERNEL_VERSION() */
+	__u32	capabilities;	/* Device capabilities */
+	__u32	reserved[4];
+};
+
+/* Values for 'capabilities' field */
+#define V4L2_CAP_VIDEO_CAPTURE	0x00000001  /* Is a video capture device */
+#define V4L2_CAP_VIDEO_OUTPUT	0x00000002  /* Is a video output device */
+#define V4L2_CAP_VIDEO_OVERLAY	0x00000004  /* Can do video overlay */
+#define V4L2_CAP_VBI_CAPTURE	0x00000010  /* Is a VBI capture device */
+#define V4L2_CAP_VBI_OUTPUT	0x00000020  /* Is a VBI output device */
+#define V4L2_CAP_RDS_CAPTURE	0x00000100  /* RDS data capture */
+
+#define V4L2_CAP_TUNER		0x00010000  /* Has a tuner */
+#define V4L2_CAP_AUDIO		0x00020000  /* has audio support */
+
+#define V4L2_CAP_READWRITE      0x01000000  /* read/write systemcalls */
+#define V4L2_CAP_ASYNCIO        0x02000000  /* async I/O */
+#define V4L2_CAP_STREAMING      0x04000000  /* streaming I/O ioctls */
+
+/*
+ *	V I D E O   I M A G E   F O R M A T
+ */
+
+struct v4l2_pix_format
+{
+	__u32         	 	width;
+	__u32	         	height;
+	__u32	         	pixelformat;
+	enum v4l2_field  	field;
+	__u32            	bytesperline;	/* for padding, zero if unused */
+	__u32          	 	sizeimage;
+        enum v4l2_colorspace	colorspace;
+	__u32			priv;		/* private data, depends on pixelformat */
+};
+
+/*           Pixel format    FOURCC                  depth  Description   */
+#define V4L2_PIX_FMT_RGB332  v4l2_fourcc('R','G','B','1') /*  8  RGB-3-3-2     */
+#define V4L2_PIX_FMT_RGB555  v4l2_fourcc('R','G','B','O') /* 16  RGB-5-5-5     */
+#define V4L2_PIX_FMT_RGB565  v4l2_fourcc('R','G','B','P') /* 16  RGB-5-6-5     */
+#define V4L2_PIX_FMT_RGB555X v4l2_fourcc('R','G','B','Q') /* 16  RGB-5-5-5 BE  */
+#define V4L2_PIX_FMT_RGB565X v4l2_fourcc('R','G','B','R') /* 16  RGB-5-6-5 BE  */
+#define V4L2_PIX_FMT_BGR24   v4l2_fourcc('B','G','R','3') /* 24  BGR-8-8-8     */
+#define V4L2_PIX_FMT_RGB24   v4l2_fourcc('R','G','B','3') /* 24  RGB-8-8-8     */
+#define V4L2_PIX_FMT_BGR32   v4l2_fourcc('B','G','R','4') /* 32  BGR-8-8-8-8   */
+#define V4L2_PIX_FMT_RGB32   v4l2_fourcc('R','G','B','4') /* 32  RGB-8-8-8-8   */
+#define V4L2_PIX_FMT_GREY    v4l2_fourcc('G','R','E','Y') /*  8  Greyscale     */
+#define V4L2_PIX_FMT_YVU410  v4l2_fourcc('Y','V','U','9') /*  9  YVU 4:1:0     */
+#define V4L2_PIX_FMT_YVU420  v4l2_fourcc('Y','V','1','2') /* 12  YVU 4:2:0     */
+#define V4L2_PIX_FMT_YUYV    v4l2_fourcc('Y','U','Y','V') /* 16  YUV 4:2:2     */
+#define V4L2_PIX_FMT_UYVY    v4l2_fourcc('U','Y','V','Y') /* 16  YUV 4:2:2     */
+#define V4L2_PIX_FMT_YUV422P v4l2_fourcc('4','2','2','P') /* 16  YVU422 planar */
+#define V4L2_PIX_FMT_YUV411P v4l2_fourcc('4','1','1','P') /* 16  YVU411 planar */
+#define V4L2_PIX_FMT_Y41P    v4l2_fourcc('Y','4','1','P') /* 12  YUV 4:1:1     */
+
+/* two planes -- one Y, one Cr + Cb interleaved  */
+#define V4L2_PIX_FMT_NV12    v4l2_fourcc('N','V','1','2') /* 12  Y/CbCr 4:2:0  */
+#define V4L2_PIX_FMT_NV21    v4l2_fourcc('N','V','2','1') /* 12  Y/CrCb 4:2:0  */
+
+/*  The following formats are not defined in the V4L2 specification */
+#define V4L2_PIX_FMT_YUV410  v4l2_fourcc('Y','U','V','9') /*  9  YUV 4:1:0     */
+#define V4L2_PIX_FMT_YUV420  v4l2_fourcc('Y','U','1','2') /* 12  YUV 4:2:0     */
+#define V4L2_PIX_FMT_YYUV    v4l2_fourcc('Y','Y','U','V') /* 16  YUV 4:2:2     */
+#define V4L2_PIX_FMT_HI240   v4l2_fourcc('H','I','2','4') /*  8  8-bit color   */
+
+/* compressed formats */
+#define V4L2_PIX_FMT_MJPEG    v4l2_fourcc('M','J','P','G') /* Motion-JPEG   */
+#define V4L2_PIX_FMT_JPEG     v4l2_fourcc('J','P','E','G') /* JFIF JPEG     */
+#define V4L2_PIX_FMT_DV       v4l2_fourcc('d','v','s','d') /* 1394          */
+#define V4L2_PIX_FMT_MPEG     v4l2_fourcc('M','P','E','G') /* MPEG          */
+
+/*  Vendor-specific formats   */
+#define V4L2_PIX_FMT_WNVA    v4l2_fourcc('W','N','V','A') /* Winnov hw compres */
+
+/*
+ *	F O R M A T   E N U M E R A T I O N
+ */
+struct v4l2_fmtdesc
+{
+	__u32	            index;             /* Format number      */
+	enum v4l2_buf_type  type;              /* buffer type        */
+	__u32               flags;
+	__u8	            description[32];   /* Description string */
+	__u32	            pixelformat;       /* Format fourcc      */
+	__u32	            reserved[4];
+};
+
+#define V4L2_FMT_FLAG_COMPRESSED 0x0001
+
+
+/*
+ *	T I M E C O D E
+ */
+struct v4l2_timecode
+{
+	__u32	type;
+	__u32	flags;
+	__u8	frames;
+	__u8	seconds;
+	__u8	minutes;
+	__u8	hours;
+	__u8	userbits[4];
+};
+
+/*  Type  */
+#define V4L2_TC_TYPE_24FPS		1
+#define V4L2_TC_TYPE_25FPS		2
+#define V4L2_TC_TYPE_30FPS		3
+#define V4L2_TC_TYPE_50FPS		4
+#define V4L2_TC_TYPE_60FPS		5
+
+/*  Flags  */
+#define V4L2_TC_FLAG_DROPFRAME		0x0001 /* "drop-frame" mode */
+#define V4L2_TC_FLAG_COLORFRAME		0x0002
+#define V4L2_TC_USERBITS_field		0x000C
+#define V4L2_TC_USERBITS_USERDEFINED	0x0000
+#define V4L2_TC_USERBITS_8BITCHARS	0x0008
+/* The above is based on SMPTE timecodes */
+
+
+/*
+ *	C O M P R E S S I O N   P A R A M E T E R S
+ */
+#if 0
+/* ### generic compression settings don't work, there is too much
+ * ### codec-specific stuff.  Maybe reuse that for MPEG codec settings
+ * ### later ... */
+struct v4l2_compression
+{
+	__u32	quality;
+	__u32	keyframerate;
+	__u32	pframerate;
+	__u32	reserved[5];
+};
+#endif
+
+struct v4l2_jpegcompression
+{
+	int quality;
+
+	int  APPn;              /* Number of APP segment to be written,
+				 * must be 0..15 */
+	int  APP_len;           /* Length of data in JPEG APPn segment */
+	char APP_data[60];      /* Data in the JPEG APPn segment. */
+	
+	int  COM_len;           /* Length of data in JPEG COM segment */
+	char COM_data[60];      /* Data in JPEG COM segment */
+	
+	__u32 jpeg_markers;     /* Which markers should go into the JPEG
+				 * output. Unless you exactly know what
+				 * you do, leave them untouched.
+				 * Inluding less markers will make the
+				 * resulting code smaller, but there will
+				 * be fewer aplications which can read it.
+				 * The presence of the APP and COM marker
+				 * is influenced by APP_len and COM_len
+				 * ONLY, not by this property! */
+	
+#define V4L2_JPEG_MARKER_DHT (1<<3)    /* Define Huffman Tables */
+#define V4L2_JPEG_MARKER_DQT (1<<4)    /* Define Quantization Tables */
+#define V4L2_JPEG_MARKER_DRI (1<<5)    /* Define Restart Interval */
+#define V4L2_JPEG_MARKER_COM (1<<6)    /* Comment segment */
+#define V4L2_JPEG_MARKER_APP (1<<7)    /* App segment, driver will
+                                        * allways use APP0 */
+};
+
+
+/*
+ *	M E M O R Y - M A P P I N G   B U F F E R S
+ */
+struct v4l2_requestbuffers
+{
+	__u32	                count;
+	enum v4l2_buf_type      type;
+	enum v4l2_memory        memory;
+	__u32	                reserved[2];
+};
+
+struct v4l2_buffer
+{
+	__u32			index;
+	enum v4l2_buf_type      type;
+	__u32			bytesused;
+	__u32			flags;
+	enum v4l2_field		field;
+	struct timeval		timestamp;
+	struct v4l2_timecode	timecode;
+	__u32			sequence;
+
+	/* memory location */
+	enum v4l2_memory        memory;
+	union {
+		__u32           offset;
+		unsigned long   userptr;
+	} m;
+	__u32			length;
+
+	__u32			reserved[2];
+};
+
+/*  Flags for 'flags' field */
+#define V4L2_BUF_FLAG_MAPPED	0x0001  /* Buffer is mapped (flag) */
+#define V4L2_BUF_FLAG_QUEUED	0x0002	/* Buffer is queued for processing */
+#define V4L2_BUF_FLAG_DONE	0x0004	/* Buffer is ready */
+#define V4L2_BUF_FLAG_KEYFRAME	0x0008	/* Image is a keyframe (I-frame) */
+#define V4L2_BUF_FLAG_PFRAME	0x0010	/* Image is a P-frame */
+#define V4L2_BUF_FLAG_BFRAME	0x0020	/* Image is a B-frame */
+#define V4L2_BUF_FLAG_TIMECODE	0x0100	/* timecode field is valid */
+
+/*
+ *	O V E R L A Y   P R E V I E W
+ */
+struct v4l2_framebuffer
+{
+	__u32			capability;
+	__u32			flags;
+/* FIXME: in theory we should pass something like PCI device + memory
+ * region + offset instead of some physical address */
+	void*                   base;
+	struct v4l2_pix_format	fmt;
+};
+/*  Flags for the 'capability' field. Read only */
+#define V4L2_FBUF_CAP_EXTERNOVERLAY	0x0001
+#define V4L2_FBUF_CAP_CHROMAKEY		0x0002
+#define V4L2_FBUF_CAP_LIST_CLIPPING     0x0004
+#define V4L2_FBUF_CAP_BITMAP_CLIPPING	0x0008
+/*  Flags for the 'flags' field. */
+#define V4L2_FBUF_FLAG_PRIMARY		0x0001
+#define V4L2_FBUF_FLAG_OVERLAY		0x0002
+#define V4L2_FBUF_FLAG_CHROMAKEY	0x0004
+
+struct v4l2_clip
+{
+	struct v4l2_rect        c;
+	struct v4l2_clip	*next;
+};
+
+struct v4l2_window
+{
+	struct v4l2_rect        w;
+	enum v4l2_field  	field;
+	__u32			chromakey;
+	struct v4l2_clip	*clips;
+	__u32			clipcount;
+	void			*bitmap;
+};
+
+
+/*
+ *	C A P T U R E   P A R A M E T E R S
+ */
+struct v4l2_captureparm
+{
+	__u32		   capability;	  /*  Supported modes */
+	__u32		   capturemode;	  /*  Current mode */
+	struct v4l2_fract  timeperframe;  /*  Time per frame in .1us units */
+	__u32		   extendedmode;  /*  Driver-specific extensions */
+	__u32              readbuffers;   /*  # of buffers for read */
+	__u32		   reserved[4];
+};
+/*  Flags for 'capability' and 'capturemode' fields */
+#define V4L2_MODE_HIGHQUALITY	0x0001	/*  High quality imaging mode */
+#define V4L2_CAP_TIMEPERFRAME	0x1000	/*  timeperframe field is supported */
+
+struct v4l2_outputparm
+{
+	__u32		   capability;	 /*  Supported modes */
+	__u32		   outputmode;	 /*  Current mode */
+	struct v4l2_fract  timeperframe; /*  Time per frame in seconds */
+	__u32		   extendedmode; /*  Driver-specific extensions */
+	__u32              writebuffers; /*  # of buffers for write */
+	__u32		   reserved[4];
+};
+
+/*
+ *	I N P U T   I M A G E   C R O P P I N G
+ */
+
+struct v4l2_cropcap {
+	enum v4l2_buf_type      type;	
+        struct v4l2_rect        bounds;
+        struct v4l2_rect        defrect;
+        struct v4l2_fract       pixelaspect;
+};
+
+struct v4l2_crop {
+	enum v4l2_buf_type      type;
+	struct v4l2_rect        c;
+};
+
+/*
+ *      A N A L O G   V I D E O   S T A N D A R D
+ */
+
+typedef __u64 v4l2_std_id;
+
+/* one bit for each */
+#define V4L2_STD_PAL_B          ((v4l2_std_id)0x00000001)
+#define V4L2_STD_PAL_B1         ((v4l2_std_id)0x00000002)
+#define V4L2_STD_PAL_G          ((v4l2_std_id)0x00000004)
+#define V4L2_STD_PAL_H          ((v4l2_std_id)0x00000008)
+#define V4L2_STD_PAL_I          ((v4l2_std_id)0x00000010)
+#define V4L2_STD_PAL_D          ((v4l2_std_id)0x00000020)
+#define V4L2_STD_PAL_D1         ((v4l2_std_id)0x00000040)
+#define V4L2_STD_PAL_K          ((v4l2_std_id)0x00000080)
+
+#define V4L2_STD_PAL_M          ((v4l2_std_id)0x00000100)
+#define V4L2_STD_PAL_N          ((v4l2_std_id)0x00000200)
+#define V4L2_STD_PAL_Nc         ((v4l2_std_id)0x00000400)
+#define V4L2_STD_PAL_60         ((v4l2_std_id)0x00000800)
+
+#define V4L2_STD_NTSC_M         ((v4l2_std_id)0x00001000)
+#define V4L2_STD_NTSC_M_JP      ((v4l2_std_id)0x00002000)
+
+#define V4L2_STD_SECAM_B        ((v4l2_std_id)0x00010000)
+#define V4L2_STD_SECAM_D        ((v4l2_std_id)0x00020000)
+#define V4L2_STD_SECAM_G        ((v4l2_std_id)0x00040000)
+#define V4L2_STD_SECAM_H        ((v4l2_std_id)0x00080000)
+#define V4L2_STD_SECAM_K        ((v4l2_std_id)0x00100000)
+#define V4L2_STD_SECAM_K1       ((v4l2_std_id)0x00200000)
+#define V4L2_STD_SECAM_L        ((v4l2_std_id)0x00400000)
+
+/* ATSC/HDTV */
+#define V4L2_STD_ATSC_8_VSB     ((v4l2_std_id)0x01000000)
+#define V4L2_STD_ATSC_16_VSB    ((v4l2_std_id)0x02000000)
+
+/* some common needed stuff */
+#define V4L2_STD_PAL_BG		(V4L2_STD_PAL_B		|\
+				 V4L2_STD_PAL_B1	|\
+				 V4L2_STD_PAL_G)
+#define V4L2_STD_PAL_DK		(V4L2_STD_PAL_D		|\
+				 V4L2_STD_PAL_D1	|\
+				 V4L2_STD_PAL_K)
+#define V4L2_STD_PAL		(V4L2_STD_PAL_BG	|\
+				 V4L2_STD_PAL_DK	|\
+				 V4L2_STD_PAL_H		|\
+				 V4L2_STD_PAL_I)
+#define V4L2_STD_NTSC           (V4L2_STD_NTSC_M	|\
+				 V4L2_STD_NTSC_M_JP)
+#define V4L2_STD_SECAM		(V4L2_STD_SECAM_B	|\
+				 V4L2_STD_SECAM_D	|\
+				 V4L2_STD_SECAM_G	|\
+				 V4L2_STD_SECAM_H	|\
+				 V4L2_STD_SECAM_K	|\
+				 V4L2_STD_SECAM_K1	|\
+				 V4L2_STD_SECAM_L)
+
+#define V4L2_STD_525_60		(V4L2_STD_PAL_M		|\
+				 V4L2_STD_PAL_60	|\
+				 V4L2_STD_NTSC)
+#define V4L2_STD_625_50		(V4L2_STD_PAL		|\
+				 V4L2_STD_PAL_N		|\
+				 V4L2_STD_PAL_Nc	|\
+				 V4L2_STD_SECAM)
+
+#define V4L2_STD_UNKNOWN        0
+#define V4L2_STD_ALL            (V4L2_STD_525_60	|\
+				 V4L2_STD_625_50)
+
+struct v4l2_standard
+{
+	__u32	       	     index;
+	v4l2_std_id          id;
+	__u8		     name[24];
+	struct v4l2_fract    frameperiod; /* Frames, not fields */
+	__u32		     framelines;
+	__u32		     reserved[4];
+};
+
+
+/*
+ *	V I D E O   I N P U T S
+ */
+struct v4l2_input
+{
+	__u32	     index;		/*  Which input */
+	__u8	     name[32];	        /*  Label */
+	__u32	     type;		/*  Type of input */
+	__u32	     audioset;	        /*  Associated audios (bitfield) */
+	__u32        tuner;             /*  Associated tuner */
+	v4l2_std_id  std;
+	__u32	     status;
+	__u32	     reserved[4];
+};
+/*  Values for the 'type' field */
+#define V4L2_INPUT_TYPE_TUNER		1
+#define V4L2_INPUT_TYPE_CAMERA		2
+
+/* field 'status' - general */
+#define V4L2_IN_ST_NO_POWER    0x00000001  /* Attached device is off */
+#define V4L2_IN_ST_NO_SIGNAL   0x00000002
+#define V4L2_IN_ST_NO_COLOR    0x00000004
+
+/* field 'status' - analog */
+#define V4L2_IN_ST_NO_H_LOCK   0x00000100  /* No horizontal sync lock */
+#define V4L2_IN_ST_COLOR_KILL  0x00000200  /* Color killer is active */
+
+/* field 'status' - digital */
+#define V4L2_IN_ST_NO_SYNC     0x00010000  /* No synchronization lock */
+#define V4L2_IN_ST_NO_EQU      0x00020000  /* No equalizer lock */
+#define V4L2_IN_ST_NO_CARRIER  0x00040000  /* Carrier recovery failed */
+
+/* field 'status' - VCR and set-top box */
+#define V4L2_IN_ST_MACROVISION 0x01000000  /* Macrovision detected */
+#define V4L2_IN_ST_NO_ACCESS   0x02000000  /* Conditional access denied */
+#define V4L2_IN_ST_VTR         0x04000000  /* VTR time constant */
+
+/*
+ *	V I D E O   O U T P U T S
+ */
+struct v4l2_output
+{
+	__u32	     index;		/*  Which output */
+	__u8	     name[32];	        /*  Label */
+	__u32	     type;		/*  Type of output */
+	__u32	     audioset;	        /*  Associated audios (bitfield) */
+	__u32	     modulator;         /*  Associated modulator */
+	v4l2_std_id  std;
+	__u32	     reserved[4];
+};
+/*  Values for the 'type' field */
+#define V4L2_OUTPUT_TYPE_MODULATOR		1
+#define V4L2_OUTPUT_TYPE_ANALOG			2
+#define V4L2_OUTPUT_TYPE_ANALOGVGAOVERLAY	3
+
+/*
+ *	C O N T R O L S
+ */
+struct v4l2_control
+{
+	__u32		     id;
+	__s32		     value;
+};
+
+/*  Used in the VIDIOC_QUERYCTRL ioctl for querying controls */
+struct v4l2_queryctrl
+{
+	__u32	             id;
+	enum v4l2_ctrl_type  type;
+	__u8		     name[32];	/* Whatever */
+	__s32		     minimum;	/* Note signedness */
+	__s32		     maximum;
+	__s32	             step;
+	__s32		     default_value;
+	__u32                flags;
+	__u32		     reserved[2];
+};
+
+/*  Used in the VIDIOC_QUERYMENU ioctl for querying menu items */
+struct v4l2_querymenu
+{
+	__u32		id;
+	__u32		index;
+	__u8		name[32];	/* Whatever */
+	__u32		reserved;
+};
+
+/*  Control flags  */
+#define V4L2_CTRL_FLAG_DISABLED		0x0001
+#define V4L2_CTRL_FLAG_GRABBED		0x0002
+
+/*  Control IDs defined by V4L2 */
+#define V4L2_CID_BASE			0x00980900
+/*  IDs reserved for driver specific controls */
+#define V4L2_CID_PRIVATE_BASE		0x08000000
+
+#define V4L2_CID_BRIGHTNESS		(V4L2_CID_BASE+0)
+#define V4L2_CID_CONTRAST		(V4L2_CID_BASE+1)
+#define V4L2_CID_SATURATION		(V4L2_CID_BASE+2)
+#define V4L2_CID_HUE			(V4L2_CID_BASE+3)
+#define V4L2_CID_AUDIO_VOLUME		(V4L2_CID_BASE+5)
+#define V4L2_CID_AUDIO_BALANCE		(V4L2_CID_BASE+6)
+#define V4L2_CID_AUDIO_BASS		(V4L2_CID_BASE+7)
+#define V4L2_CID_AUDIO_TREBLE		(V4L2_CID_BASE+8)
+#define V4L2_CID_AUDIO_MUTE		(V4L2_CID_BASE+9)
+#define V4L2_CID_AUDIO_LOUDNESS		(V4L2_CID_BASE+10)
+#define V4L2_CID_BLACK_LEVEL		(V4L2_CID_BASE+11)
+#define V4L2_CID_AUTO_WHITE_BALANCE	(V4L2_CID_BASE+12)
+#define V4L2_CID_DO_WHITE_BALANCE	(V4L2_CID_BASE+13)
+#define V4L2_CID_RED_BALANCE		(V4L2_CID_BASE+14)
+#define V4L2_CID_BLUE_BALANCE		(V4L2_CID_BASE+15)
+#define V4L2_CID_GAMMA			(V4L2_CID_BASE+16)
+#define V4L2_CID_WHITENESS		(V4L2_CID_GAMMA) /* ? Not sure */
+#define V4L2_CID_EXPOSURE		(V4L2_CID_BASE+17)
+#define V4L2_CID_AUTOGAIN		(V4L2_CID_BASE+18)
+#define V4L2_CID_GAIN			(V4L2_CID_BASE+19)
+#define V4L2_CID_HFLIP			(V4L2_CID_BASE+20)
+#define V4L2_CID_VFLIP			(V4L2_CID_BASE+21)
+#define V4L2_CID_HCENTER		(V4L2_CID_BASE+22)
+#define V4L2_CID_VCENTER		(V4L2_CID_BASE+23)
+#define V4L2_CID_LASTP1			(V4L2_CID_BASE+24) /* last CID + 1 */
+
+/*
+ *	T U N I N G
+ */
+struct v4l2_tuner
+{
+	__u32                   index;
+	__u8			name[32];
+	enum v4l2_tuner_type    type;
+	__u32			capability;
+	__u32			rangelow;
+	__u32			rangehigh;
+	__u32			rxsubchans;
+	__u32			audmode;
+	__s32			signal;
+	__s32			afc;
+	__u32			reserved[4];
+};
+
+struct v4l2_modulator
+{
+	__u32			index;
+	__u8			name[32];
+	__u32			capability;
+	__u32			rangelow;
+	__u32			rangehigh;
+	__u32			txsubchans;
+	__u32			reserved[4];
+};
+
+/*  Flags for the 'capability' field */
+#define V4L2_TUNER_CAP_LOW		0x0001
+#define V4L2_TUNER_CAP_NORM		0x0002
+#define V4L2_TUNER_CAP_STEREO		0x0010
+#define V4L2_TUNER_CAP_LANG2		0x0020
+#define V4L2_TUNER_CAP_SAP		0x0020
+#define V4L2_TUNER_CAP_LANG1		0x0040
+
+/*  Flags for the 'rxsubchans' field */
+#define V4L2_TUNER_SUB_MONO		0x0001
+#define V4L2_TUNER_SUB_STEREO		0x0002
+#define V4L2_TUNER_SUB_LANG2		0x0004
+#define V4L2_TUNER_SUB_SAP		0x0004
+#define V4L2_TUNER_SUB_LANG1		0x0008
+
+/*  Values for the 'audmode' field */
+#define V4L2_TUNER_MODE_MONO		0x0000
+#define V4L2_TUNER_MODE_STEREO		0x0001
+#define V4L2_TUNER_MODE_LANG2		0x0002
+#define V4L2_TUNER_MODE_SAP		0x0002
+#define V4L2_TUNER_MODE_LANG1		0x0003
+
+struct v4l2_frequency
+{
+	__u32	              tuner;
+	enum v4l2_tuner_type  type;
+        __u32	              frequency;
+	__u32	              reserved[8];
+};
+
+/*
+ *	A U D I O
+ */
+struct v4l2_audio
+{
+	__u32	index;
+	__u8	name[32];
+	__u32	capability;
+	__u32	mode;
+	__u32	reserved[2];
+};
+/*  Flags for the 'capability' field */
+#define V4L2_AUDCAP_STEREO		0x00001
+#define V4L2_AUDCAP_AVL			0x00002
+
+/*  Flags for the 'mode' field */
+#define V4L2_AUDMODE_AVL		0x00001
+
+struct v4l2_audioout
+{
+	__u32	index;
+	__u8	name[32];
+	__u32	capability;
+	__u32	mode;
+	__u32	reserved[2];
+};
+
+/*
+ *	D A T A   S E R V I C E S   ( V B I )
+ *
+ *	Data services API by Michael Schimek
+ */
+
+struct v4l2_vbi_format
+{
+	__u32	sampling_rate;		/* in 1 Hz */
+	__u32	offset;
+	__u32	samples_per_line;
+	__u32	sample_format;		/* V4L2_PIX_FMT_* */
+	__s32	start[2];
+	__u32	count[2];
+	__u32	flags;			/* V4L2_VBI_* */
+	__u32	reserved[2];		/* must be zero */
+};
+
+/*  VBI flags  */
+#define V4L2_VBI_UNSYNC		(1<< 0)
+#define V4L2_VBI_INTERLACED	(1<< 1)
+
+
+/*
+ *	A G G R E G A T E   S T R U C T U R E S
+ */
+
+/*	Stream data format
+ */
+struct v4l2_format
+{
+	enum v4l2_buf_type type;
+	union
+	{
+		struct v4l2_pix_format	pix;  // V4L2_BUF_TYPE_VIDEO_CAPTURE
+		struct v4l2_window	win;  // V4L2_BUF_TYPE_VIDEO_OVERLAY
+		struct v4l2_vbi_format	vbi;  // V4L2_BUF_TYPE_VBI_CAPTURE
+		__u8	raw_data[200];        // user-defined
+	} fmt;
+};
+
+
+/*	Stream type-dependent parameters
+ */
+struct v4l2_streamparm
+{
+	enum v4l2_buf_type type;
+	union
+	{
+		struct v4l2_captureparm	capture;
+		struct v4l2_outputparm	output;
+		__u8	raw_data[200];  /* user-defined */
+	} parm;
+};
+
+
+
+/*
+ *	I O C T L   C O D E S   F O R   V I D E O   D E V I C E S
+ *
+ */
+#define VIDIOC_QUERYCAP		_IOR  ('V',  0, struct v4l2_capability)
+#define VIDIOC_RESERVED		_IO   ('V',  1)
+#define VIDIOC_ENUM_FMT         _IOWR ('V',  2, struct v4l2_fmtdesc)
+#define VIDIOC_G_FMT		_IOWR ('V',  4, struct v4l2_format)
+#define VIDIOC_S_FMT		_IOWR ('V',  5, struct v4l2_format)
+#if 0
+#define VIDIOC_G_COMP		_IOR  ('V',  6, struct v4l2_compression)
+#define VIDIOC_S_COMP		_IOW  ('V',  7, struct v4l2_compression)
+#endif
+#define VIDIOC_REQBUFS		_IOWR ('V',  8, struct v4l2_requestbuffers)
+#define VIDIOC_QUERYBUF		_IOWR ('V',  9, struct v4l2_buffer)
+#define VIDIOC_G_FBUF		_IOR  ('V', 10, struct v4l2_framebuffer)
+#define VIDIOC_S_FBUF		_IOW  ('V', 11, struct v4l2_framebuffer)
+#define VIDIOC_OVERLAY		_IOWR ('V', 14, int)
+#define VIDIOC_QBUF		_IOWR ('V', 15, struct v4l2_buffer)
+#define VIDIOC_DQBUF		_IOWR ('V', 17, struct v4l2_buffer)
+#define VIDIOC_STREAMON		_IOW  ('V', 18, int)
+#define VIDIOC_STREAMOFF	_IOW  ('V', 19, int)
+#define VIDIOC_G_PARM		_IOWR ('V', 21, struct v4l2_streamparm)
+#define VIDIOC_S_PARM		_IOW  ('V', 22, struct v4l2_streamparm)
+#define VIDIOC_G_STD		_IOR  ('V', 23, v4l2_std_id)
+#define VIDIOC_S_STD		_IOW  ('V', 24, v4l2_std_id)
+#define VIDIOC_ENUMSTD		_IOWR ('V', 25, struct v4l2_standard)
+#define VIDIOC_ENUMINPUT	_IOWR ('V', 26, struct v4l2_input)
+#define VIDIOC_G_CTRL		_IOWR ('V', 27, struct v4l2_control)
+#define VIDIOC_S_CTRL		_IOW  ('V', 28, struct v4l2_control)
+#define VIDIOC_G_TUNER		_IOWR ('V', 29, struct v4l2_tuner)
+#define VIDIOC_S_TUNER		_IOW  ('V', 30, struct v4l2_tuner)
+#define VIDIOC_G_AUDIO		_IOWR ('V', 33, struct v4l2_audio)
+#define VIDIOC_S_AUDIO		_IOW  ('V', 34, struct v4l2_audio)
+#define VIDIOC_QUERYCTRL	_IOWR ('V', 36, struct v4l2_queryctrl)
+#define VIDIOC_QUERYMENU	_IOWR ('V', 37, struct v4l2_querymenu)
+#define VIDIOC_G_INPUT		_IOR  ('V', 38, int)
+#define VIDIOC_S_INPUT		_IOWR ('V', 39, int)
+#define VIDIOC_G_OUTPUT		_IOR  ('V', 46, int)
+#define VIDIOC_S_OUTPUT		_IOWR ('V', 47, int)
+#define VIDIOC_ENUMOUTPUT	_IOWR ('V', 48, struct v4l2_output)
+#define VIDIOC_G_AUDOUT		_IOWR ('V', 49, struct v4l2_audioout)
+#define VIDIOC_S_AUDOUT		_IOW  ('V', 50, struct v4l2_audioout)
+#define VIDIOC_G_MODULATOR	_IOWR ('V', 54, struct v4l2_modulator)
+#define VIDIOC_S_MODULATOR	_IOW  ('V', 55, struct v4l2_modulator)
+#define VIDIOC_G_FREQUENCY	_IOWR ('V', 56, struct v4l2_frequency)
+#define VIDIOC_S_FREQUENCY	_IOW  ('V', 57, struct v4l2_frequency)
+#define VIDIOC_CROPCAP		_IOR  ('V', 58, struct v4l2_cropcap)
+#define VIDIOC_G_CROP		_IOWR ('V', 59, struct v4l2_crop)
+#define VIDIOC_S_CROP		_IOW  ('V', 60, struct v4l2_crop)
+#define VIDIOC_G_JPEGCOMP	_IOR  ('V', 61, struct v4l2_jpegcompression)
+#define VIDIOC_S_JPEGCOMP	_IOW  ('V', 62, struct v4l2_jpegcompression)
+#define VIDIOC_QUERYSTD      	_IOR  ('V', 63, v4l2_std_id)
+#define VIDIOC_TRY_FMT      	_IOWR ('V', 64, struct v4l2_format)
+
+#define BASE_VIDIOC_PRIVATE	192		/* 192-255 are private */
+
+
+#ifdef __KERNEL__
+/*
+ *
+ *	V 4 L 2   D R I V E R   H E L P E R   A P I
+ *
+ *	Some commonly needed functions for drivers (v4l2-common.o module)
+ */
+#include <linux/fs.h>
+
+/*  Video standard functions  */
+extern unsigned int v4l2_video_std_fps(struct v4l2_standard *vs);
+extern int v4l2_video_std_construct(struct v4l2_standard *vs,
+				    int id, char *name);
+
+/*  Compatibility layer interface  */
+typedef int (*v4l2_kioctl)(struct inode *inode, struct file *file,
+			   unsigned int cmd, void *arg);
+int v4l_compat_translate_ioctl(struct inode *inode, struct file *file,
+			       int cmd, void *arg, v4l2_kioctl driver_ioctl);
+
+/* names for fancy debug output */
+extern char *v4l2_field_names[];
+extern char *v4l2_type_names[];
+extern char *v4l2_ioctl_names[];
+
+#endif /* __KERNEL__ */
+#endif /* __LINUX_VIDEODEV2_H */
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */