aboutsummaryrefslogtreecommitdiffhomepage
path: root/libmpdemux
diff options
context:
space:
mode:
authorGravatar henry <henry@b3059339-0415-0410-9bf9-f77b7e298cf2>2003-08-07 12:18:04 +0000
committerGravatar henry <henry@b3059339-0415-0410-9bf9-f77b7e298cf2>2003-08-07 12:18:04 +0000
commite07e36bbe4e605aff64f68e28cefe7676308c83f (patch)
treecb0a3a4198a580060704a5612dd25df85b306b7a /libmpdemux
parent21382f9486db61a3e2a34ff9cb3721a529f01f9d (diff)
v4l2 support
git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@10537 b3059339-0415-0410-9bf9-f77b7e298cf2
Diffstat (limited to 'libmpdemux')
-rw-r--r--libmpdemux/tvi_v4l2.c1622
-rw-r--r--libmpdemux/videodev2.h862
2 files changed, 2484 insertions, 0 deletions
diff --git a/libmpdemux/tvi_v4l2.c b/libmpdemux/tvi_v4l2.c
new file mode 100644
index 0000000000..f537195544
--- /dev/null
+++ b/libmpdemux/tvi_v4l2.c
@@ -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 */
diff --git a/libmpdemux/videodev2.h b/libmpdemux/videodev2.h
new file mode 100644
index 0000000000..a4b8276e09
--- /dev/null
+++ b/libmpdemux/videodev2.h
@@ -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:
+ */