changeset 2802:09d5c9834580

tv update
author alex
date Sat, 10 Nov 2001 23:32:10 +0000
parents 318c240363c7
children 470d571ccb16
files cfg-common.h libmpdemux/demuxer.c libmpdemux/open.c libmpdemux/tv.c libmpdemux/tv.h libmpdemux/tvi_def.h libmpdemux/tvi_dummy.c libmpdemux/tvi_v4l.c
diffstat 8 files changed, 732 insertions(+), 159 deletions(-) [+]
line wrap: on
line diff
--- a/cfg-common.h	Sat Nov 10 23:28:10 2001 +0000
+++ b/cfg-common.h	Sat Nov 10 23:32:10 2001 +0000
@@ -94,11 +94,12 @@
 	{"on", &tv_param_on, CONF_TYPE_FLAG, 0, 0, 1},
 	{"driver", &tv_param_driver, CONF_TYPE_STRING, 0, 0, 0},
 	{"device", &tv_param_device, CONF_TYPE_STRING, 0, 0, 0},
-	{"freq", &tv_param_freq, CONF_TYPE_FLOAT, CONF_RANGE, 0, 2500000},
+	{"freq", &tv_param_freq, CONF_TYPE_INT, CONF_RANGE, 0, 2500000},
 	{"channel", &tv_param_channel, CONF_TYPE_STRING, 0, 0, 0},
 	{"norm", &tv_param_norm, CONF_TYPE_STRING, 0, 0, 0},
 	{"width", &tv_param_width, CONF_TYPE_INT, 0, 0, 4096},
 	{"height", &tv_param_height, CONF_TYPE_INT, 0, 0, 4096},
+	{"input", &tv_param_input, CONF_TYPE_INT, 0, 0, 20},
 	{NULL, NULL, 0, 0, 0, 0}
 };
 #endif
--- a/libmpdemux/demuxer.c	Sat Nov 10 23:28:10 2001 +0000
+++ b/libmpdemux/demuxer.c	Sat Nov 10 23:32:10 2001 +0000
@@ -158,7 +158,9 @@
 int demux_mov_fill_buffer(demuxer_t *demux,demux_stream_t* ds);
 int demux_vivo_fill_buffer(demuxer_t *demux);
 #ifdef USE_TV
-int demux_tv_fill_buffer(demuxer_t *demux);
+#include "tv.h"
+extern tvi_handle_t *tv_handler;
+int demux_tv_fill_buffer(demuxer_t *demux, tvi_handle_t *tvh);
 #endif
 
 int demux_fill_buffer(demuxer_t *demux,demux_stream_t *ds){
@@ -174,7 +176,7 @@
     case DEMUXER_TYPE_MOV: return demux_mov_fill_buffer(demux,ds);
     case DEMUXER_TYPE_VIVO: return demux_vivo_fill_buffer(demux);
 #ifdef USE_TV
-    case DEMUXER_TYPE_TV: return demux_tv_fill_buffer(demux);
+    case DEMUXER_TYPE_TV: return demux_tv_fill_buffer(demux, tv_handler);
 #endif
   }
   return 0;
@@ -554,7 +556,7 @@
  }
 #ifdef USE_TV
  case DEMUXER_TYPE_TV: {
-    demux_open_tv(demuxer);
+    demux_open_tv(demuxer, tv_handler);
     break;
  }
 #endif
--- a/libmpdemux/open.c	Sat Nov 10 23:28:10 2001 +0000
+++ b/libmpdemux/open.c	Sat Nov 10 23:32:10 2001 +0000
@@ -72,7 +72,7 @@
 
 #ifdef USE_TV
 #include "tv.h"
-extern tvi_handle_t *tv_handler;
+tvi_handle_t *tv_handler;
 #endif
 
 // Open a new stream  (stdin/file/vcd/url)
@@ -251,12 +251,13 @@
 //============ Check for TV-input ====
   if (tv_param_on==1)
   {
-    stream=new_stream(-1,STREAMTYPE_TV);
+    stream = new_stream(-1,STREAMTYPE_TV);
     tv_handler = tv_begin();
     if (!tv_handler)
 	return(NULL);
-    tv_init(tv_handler);
-    return(stream);
+    if (tv_init(tv_handler) == 1)
+	return(stream);
+    return(NULL);
   }
 #endif
 
--- a/libmpdemux/tv.c	Sat Nov 10 23:28:10 2001 +0000
+++ b/libmpdemux/tv.c	Sat Nov 10 23:32:10 2001 +0000
@@ -13,19 +13,16 @@
 #include "config.h"
 
 #ifdef USE_TV
-#include "tv.h"
 #include "mp_msg.h"
 #include "help_mp.h"
 
 #include "stream.h"
 #include "demuxer.h"
 #include "stheader.h"
-
-/* global! */
-tvi_handle_t *tv_handler;
+#include "tv.h"
 
 /* some default values */
-float tv_param_freq = 0.0;
+unsigned long tv_param_freq = 0;
 char *tv_param_channel = "0";
 char *tv_param_norm = "pal";
 int tv_param_on = 0;
@@ -33,6 +30,7 @@
 char *tv_param_driver = "dummy";
 int tv_param_width = -1;
 int tv_param_height = -1;
+int tv_param_input = 0; /* used in v4l and bttv */
 
 
 /* ================== DEMUX_TV ===================== */
@@ -42,25 +40,32 @@
     1 = successfully read a packet
 */
 /* fill demux->video and demux->audio */
-int demux_tv_fill_buffer(demuxer_t *demux)
+int demux_tv_fill_buffer(demuxer_t *demux, tvi_handle_t *tvh)
 {
-    int seq;
+    int seq = tvh->seq;
     demux_stream_t *ds_video = NULL;
     demux_packet_t *dp_video = NULL;
     demux_stream_t *ds_audio = NULL;
     demux_packet_t *dp_audio = NULL;
     int len_video, len_audio;
 
+    printf("demux_tv_fill_buffer(sequence:%d) called!\n", seq);
+
     demux->filepos = -1;
 
+    seq++;
+    tvh->seq++;
+
     /* ================== ADD VIDEO PACKET =================== */
-    len_video = tv_handler->functions->get_video_framesize(tv_handler->priv);
+    len_video = tvh->functions->get_video_framesize(tvh->priv);
     ds_video = demux->video;
 
-    if (!ds_video)
-    {    
+    if (!ds_video->asf_packet)
+    {
+	/* create new packet */
 	dp_video = new_demux_packet(len_video);
-	tv_handler->functions->grab_video_frame(tv_handler->priv, dp_video->buffer, len_video);
+//	printf("new dp_video->buffer: %p (%d bytes)\n", dp_video, len_video);
+	tvh->functions->grab_video_frame(tvh->priv, dp_video->buffer, len_video);
 	dp_video->pos = demux->filepos;
 	ds_video->asf_packet = dp_video;
 	ds_video->asf_seq = seq;
@@ -69,14 +74,17 @@
     {
 	if (ds_video->asf_seq != seq)
 	{
+	    /* close segment, finalize packet */
 	    ds_add_packet(ds_video, ds_video->asf_packet);
 	    ds_video->asf_packet = NULL;
 	}
 	else
 	{
+	    /* append data to segment */
 	    dp_video = ds_video->asf_packet;
 	    dp_video->buffer = realloc(dp_video->buffer, dp_video->len+len_video);
-	    tv_handler->functions->grab_video_frame(tv_handler->priv, dp_video->buffer+dp_video->len, len_video);
+//	    printf("dp_video->buffer: %p (%d bytes)\n", dp_video, dp_video->len+len_video);
+	    tvh->functions->grab_video_frame(tvh->priv, dp_video->buffer+dp_video->len, len_video);
 	    mp_dbg(MSGT_DEMUX,MSGL_DBG4, "video data appended %d+%d\n", dp_video->len, len_video);
 	    dp_video->len += len_video;
 	}
@@ -84,13 +92,16 @@
     
 
     /* ================== ADD AUDIO PACKET =================== */
-    len_audio = tv_handler->functions->get_audio_framesize(tv_handler->priv);
+    if (tvh->functions->control(tvh->priv, TVI_CONTROL_IS_AUDIO, 0) != TVI_CONTROL_TRUE)
+	return 1; /* no audio, only video */
+
+    len_audio = tvh->functions->get_audio_framesize(tvh->priv);
     ds_audio = demux->audio;
     
-    if (!ds_audio)
+    if (!ds_audio->asf_packet)
     {
 	dp_audio = new_demux_packet(len_audio);
-	tv_handler->functions->grab_audio_frame(tv_handler->priv, dp_audio->buffer, len_audio);
+	tvh->functions->grab_audio_frame(tvh->priv, dp_audio->buffer, len_audio);
 	dp_audio->pos = demux->filepos;
 	ds_audio->asf_packet = dp_audio;
 	ds_audio->asf_seq = seq;
@@ -106,7 +117,7 @@
 	{
 	    dp_audio = ds_audio->asf_packet;
 	    dp_audio->buffer = realloc(dp_audio->buffer, dp_audio->len+len_audio);
-	    tv_handler->functions->grab_audio_frame(tv_handler->priv, dp_audio->buffer+dp_audio->len, len_audio);
+	    tvh->functions->grab_audio_frame(tvh->priv, dp_audio->buffer+dp_audio->len, len_audio);
 	    mp_dbg(MSGT_DEMUX,MSGL_DBG4, "audio data appended %d+%d\n", dp_audio->len, len_audio);
 	    dp_audio->len += len_audio;
 	}
@@ -115,18 +126,30 @@
     return 1;
 }
 
-int demux_open_tv(demuxer_t *demuxer)
+int demux_open_tv(demuxer_t *demuxer, tvi_handle_t *tvh)
 {
-    sh_video_t *sh_video;
-    tvi_handle_t *tvh = tv_handler;
+    sh_video_t *sh_video = NULL;
+    sh_audio_t *sh_audio = NULL;
     tvi_functions_t *funcs = tvh->functions;
     
-    sh_video = new_sh_video(demuxer,0);
+    if (funcs->control(tvh->priv, TVI_CONTROL_IS_VIDEO, 0) != TVI_CONTROL_TRUE)
+    {
+	printf("Error: no video input present!\n");
+	return;
+    }
+    
+    sh_video = new_sh_video(demuxer, 0);
 
-//    sh->format=0x7476696e; /* "tvin" */
-    if (funcs->control(tvh->priv, TVI_CONTROL_VID_GET_FORMAT, &sh_video->format) != TVI_CONTROL_TRUE)
-	sh_video->format = 0x00000000;
+    /* hack to use YUV 4:2:0 format ;) */
+    sh_video->format = IMGFMT_YV12;
+    funcs->control(tvh->priv, TVI_CONTROL_VID_SET_FORMAT, &sh_video->format);
 
+    /* get IMGFMT_ */
+    funcs->control(tvh->priv, TVI_CONTROL_VID_GET_FORMAT, &sh_video->format);
+    if (IMGFMT_IS_RGB(sh_video->format) || IMGFMT_IS_BGR(sh_video->format))
+	sh_video->format = 0x0;
+
+    /* set FPS and FRAMETIME */
     if(!sh_video->fps)
     {
 	if (funcs->control(tvh->priv, TVI_CONTROL_VID_GET_FPS, &sh_video->fps) != TVI_CONTROL_TRUE)
@@ -169,47 +192,100 @@
     }
     else
 	funcs->control(tvh->priv, TVI_CONTROL_VID_GET_HEIGHT, &sh_video->disp_h);
-    
-    /* emulate BITMAPINFOHEADER */
-    sh_video->bih = malloc(sizeof(BITMAPINFOHEADER));
-    memset(sh_video->bih, 0, sizeof(BITMAPINFOHEADER));
-    sh_video->bih->biSize = 40;
-    sh_video->bih->biWidth = sh_video->disp_w;
-    sh_video->bih->biHeight = sh_video->disp_h;
-    if (funcs->control(tvh->priv, TVI_CONTROL_VID_GET_PLANES, &sh_video->bih->biPlanes) != TVI_CONTROL_TRUE)
-	sh_video->bih->biPlanes = 1;
-    if (funcs->control(tvh->priv, TVI_CONTROL_VID_GET_BITS, &sh_video->bih->biBitCount) != TVI_CONTROL_TRUE)
-	sh_video->bih->biBitCount = 12;
-    sh_video->bih->biCompression = sh_video->format;
-    sh_video->bih->biSizeImage = sh_video->bih->biWidth * sh_video->bih->biHeight * 3;
+
+    printf("Output size: %dx%d\n", sh_video->disp_w, sh_video->disp_h);
     
     demuxer->video->sh = sh_video;
     sh_video->ds = demuxer->video;
     demuxer->video->id = 0;
 
     /* here comes audio init */
+    if (funcs->control(tvh->priv, TVI_CONTROL_IS_AUDIO, 0) == TVI_CONTROL_TRUE)
+    {
+	int audio_format;
+
+	sh_audio = new_sh_audio(demuxer, 0);
+	
+	sh_audio->wf = malloc(sizeof(WAVEFORMATEX));
+	memset(sh_audio->wf, 0, sizeof(WAVEFORMATEX));
+	
+	/* yeah, audio is present */
+	if (funcs->control(tvh->priv, TVI_CONTROL_AUD_GET_FORMAT, &audio_format) != TVI_CONTROL_TRUE)
+	    goto no_audio;
+	switch(audio_format)
+	{
+	    case AFMT_U8:
+	    case AFMT_S8:
+	    case AFMT_U16_LE:
+	    case AFMT_U16_BE:
+	    case AFMT_S16_LE:
+	    case AFMT_S16_BE:
+	    case AFMT_S32_LE:
+	    case AFMT_S32_BE:
+		sh_audio->format = 0x1; /* PCM */
+		break;
+	    case AFMT_IMA_ADPCM:
+	    case AFMT_MU_LAW:
+	    case AFMT_A_LAW:
+	    case AFMT_MPEG:
+	    case AFMT_AC3:
+	    default:
+		printf("%s unsupported!\n", audio_out_format_name(audio_format));
+		goto no_audio;
+	}
+	
+	funcs->control(tvh->priv, TVI_CONTROL_AUD_GET_CHANNELS, &sh_audio->wf->nChannels);
+	funcs->control(tvh->priv, TVI_CONTROL_AUD_GET_SAMPLERATE, &sh_audio->wf->nSamplesPerSec);
+	funcs->control(tvh->priv, TVI_CONTROL_AUD_GET_SAMPLESIZE, &sh_audio->wf->nAvgBytesPerSec);
+
+	demuxer->audio->sh = sh_audio;
+	sh_audio->ds = demuxer->audio;
+	demuxer->audio->id = 0;
+    }
+no_audio:
+
+    /* set some params got from cmdline */
+    funcs->control(tvh->priv, TVI_CONTROL_SPC_SET_INPUT, &tv_param_input);
+
+    /* set freq in MHz - change this to float ! */
+    funcs->control(tvh->priv, TVI_CONTROL_TUN_SET_FREQ, &tv_param_freq);
+
+    funcs->control(tvh->priv, TVI_CONTROL_TUN_GET_FREQ, &tv_param_freq);
+    printf("freq: %lu\n", tv_param_freq);
+    
+    /* also start device! */
+    funcs->start(tvh->priv);
 }
 
 /* ================== STREAM_TV ===================== */
-tvi_handle_t *tv_begin()
+tvi_handle_t *tv_begin(void)
 {
     if (!strcmp(tv_param_driver, "dummy"))
-	return tvi_init_dummy(tv_param_device);
+	return (tvi_handle_t *)tvi_init_dummy(tv_param_device);
     if (!strcmp(tv_param_driver, "v4l"))
-	return tvi_init_v4l(tv_param_device);
+	return (tvi_handle_t *)tvi_init_v4l(tv_param_device);
 
     mp_msg(MSGT_TV, MSGL_ERR, "No such driver: %s\n", tv_param_driver); 
     return(NULL);
 }
 
-void tv_init(tvi_handle_t *tvi)
+int tv_init(tvi_handle_t *tvh)
 {
-    printf("Using driver: %s\n", tvi->info->short_name);
-    printf(" name: %s\n", tvi->info->name);
-    printf(" author: %s\n", tvi->info->author);
-    if (tvi->info->comment)
-	printf(" comment: %s\n", tvi->info->comment);
+    tvi_param_t *params;
+
+    printf("Selected driver: %s\n", tvh->info->short_name);
+    printf(" name: %s\n", tvh->info->name);
+    printf(" author: %s\n", tvh->info->author);
+    if (tvh->info->comment)
+	printf(" comment: %s\n", tvh->info->comment);
 
-    return tvi->functions->init(tvi->priv);
+    params = malloc(sizeof(tvi_param_t)*2);
+    params[0].opt = malloc(strlen("input"));
+    sprintf((char *)params[0].opt, "input");
+    params[0].value = malloc(sizeof(int));
+    (int)*(void **)params[0].value = tv_param_input;
+    params[1].opt = params[1].value = NULL;
+
+    return tvh->functions->init(tvh->priv, params);
 }
 #endif /* USE_TV */
--- a/libmpdemux/tv.h	Sat Nov 10 23:28:10 2001 +0000
+++ b/libmpdemux/tv.h	Sat Nov 10 23:32:10 2001 +0000
@@ -1,7 +1,10 @@
 #include "config.h"
 
 #ifdef USE_TV
-extern float tv_param_freq;
+#include "../libao2/afmt.h"
+#include "../libvo/img_format.h"
+
+extern unsigned long tv_param_freq;
 extern char *tv_param_channel;
 extern char *tv_param_norm;
 extern int tv_param_on;
@@ -9,6 +12,7 @@
 extern char *tv_param_driver;
 extern int tv_param_width;
 extern int tv_param_height;
+extern int tv_param_input;
 
 typedef struct tvi_info_s
 {
@@ -21,42 +25,78 @@
 typedef struct tvi_functions_s
 {
     int (*init)();
-    int (*exit)();
+    int (*uninit)();
     int (*control)();
+    int (*start)();
     int (*grab_video_frame)();
     int (*get_video_framesize)();
     int (*grab_audio_frame)();
     int (*get_audio_framesize)();
 } tvi_functions_t;
 
+typedef struct tvi_param_s {
+    const char	*opt;
+    void 	*value;
+} tvi_param_t;
+
 typedef struct tvi_handle_s {
-    tvi_info_t *info;
-    tvi_functions_t *functions;
-    void *priv;
+    tvi_info_t		*info;
+    tvi_functions_t	*functions;
+    void		*priv;
+    tvi_param_t		*params;
+    int 		seq;
 } tvi_handle_t;
 
+
 #define TVI_CONTROL_FALSE		0
 #define TVI_CONTROL_TRUE		1
 #define TVI_CONTROL_NA			-1
 #define TVI_CONTROL_UNKNOWN		-2
 
+/* ======================== CONTROLS =========================== */
 
-#define TVI_CONTROL_VID_GET_FPS		1
-#define TVI_CONTROL_VID_GET_PLANES	2
-#define TVI_CONTROL_VID_GET_BITS	3
-#define TVI_CONTROL_VID_CHK_BITS	4
-#define TVI_CONTROL_VID_SET_BITS	5
-#define TVI_CONTROL_VID_GET_FORMAT	6
-#define TVI_CONTROL_VID_CHK_FORMAT	7
-#define TVI_CONTROL_VID_SET_FORMAT	8
-#define TVI_CONTROL_VID_GET_WIDTH	9
-#define TVI_CONTROL_VID_CHK_WIDTH	10
-#define TVI_CONTROL_VID_SET_WIDTH	11
-#define TVI_CONTROL_VID_GET_HEIGHT	12
-#define TVI_CONTROL_VID_CHK_HEIGHT	13
-#define TVI_CONTROL_VID_SET_HEIGHT	14
+/* GENERIC controls */
+#define TVI_CONTROL_IS_AUDIO		0x1
+#define TVI_CONTROL_IS_VIDEO		0x2
+#define TVI_CONTROL_IS_TUNER		0x3
+
+/* VIDEO controls */
+#define TVI_CONTROL_VID_GET_FPS		0x101
+#define TVI_CONTROL_VID_GET_PLANES	0x102
+#define TVI_CONTROL_VID_GET_BITS	0x103
+#define TVI_CONTROL_VID_CHK_BITS	0x104
+#define TVI_CONTROL_VID_SET_BITS	0x105
+#define TVI_CONTROL_VID_GET_FORMAT	0x106
+#define TVI_CONTROL_VID_CHK_FORMAT	0x107
+#define TVI_CONTROL_VID_SET_FORMAT	0x108
+#define TVI_CONTROL_VID_GET_WIDTH	0x109
+#define TVI_CONTROL_VID_CHK_WIDTH	0x110
+#define TVI_CONTROL_VID_SET_WIDTH	0x111
+#define TVI_CONTROL_VID_GET_HEIGHT	0x112
+#define TVI_CONTROL_VID_CHK_HEIGHT	0x113
+#define TVI_CONTROL_VID_SET_HEIGHT	0x114
 
-#define TVI_CONTROL_TUN_GET_FREQ	100
-#define TVI_CONTROL_TUN_SET_FREQ	101
+/* TUNER controls */
+#define TVI_CONTROL_TUN_GET_FREQ	0x201
+#define TVI_CONTROL_TUN_SET_FREQ	0x202
+#define TVI_CONTROL_TUN_GET_TUNER	0x203	/* update priv->tuner struct for used input */
+#define TVI_CONTROL_TUN_SET_TUNER	0x204	/* update priv->tuner struct for used input */
+#define TVI_CONTROL_TUN_GET_NORM	0x205
+#define TVI_CONTROL_TUN_SET_NORM	0x206
+
+/* AUDIO controls */
+#define TVI_CONTROL_AUD_GET_FORMAT	0x301
+#define TVI_CONTROL_AUD_GET_SAMPLERATE	0x302
+#define TVI_CONTROL_AUD_GET_SAMPLESIZE	0x303
+#define TVI_CONTROL_AUD_GET_CHANNELS	0x304
+
+/* SPECIFIC controls */
+#define TVI_CONTROL_SPC_GET_INPUT	0x401	/* set input channel (tv,s-video,composite..) */
+#define TVI_CONTROL_SPC_SET_INPUT	0x402	/* set input channel (tv,s-video,composite..) */
+
+//extern int demux_tv_fill_buffer(demuxer_t *demux, tvi_handle_t *tvh);
+//extern int demux_open_tv(demuxer_t *demux, tvi_handle_t *tvh);
+extern tvi_handle_t *tv_begin(void);
+extern int tv_init(tvi_handle_t *tvh);
 
 #endif /* USE_TV */
--- a/libmpdemux/tvi_def.h	Sat Nov 10 23:28:10 2001 +0000
+++ b/libmpdemux/tvi_def.h	Sat Nov 10 23:32:10 2001 +0000
@@ -1,6 +1,7 @@
-static int init(priv_t *priv);
-static int exit(priv_t *priv);
+static int init(priv_t *priv, tvi_param_t *params);
+static int uninit(priv_t *priv);
 static int control(priv_t *priv, int cmd, void *arg);
+static int start(priv_t *priv);
 static int grab_video_frame(priv_t *priv, char *buffer, int len);
 static int get_video_framesize(priv_t *priv);
 static int grab_audio_frame(priv_t *priv, char *buffer, int len);
@@ -9,8 +10,9 @@
 static tvi_functions_t functions =
 {
     init,
-    exit,
+    uninit,
     control,
+    start,
     grab_video_frame,
     get_video_framesize,
     grab_audio_frame,
@@ -23,7 +25,7 @@
 
     if (!h)
 	return(NULL);
-    h->priv = malloc(sizeof(priv_t));
+    h->priv = (priv_t *)malloc(sizeof(priv_t));
     if (!h->priv)
     {
 	free(h);
@@ -32,6 +34,8 @@
     memset(h->priv, 0, sizeof(priv_t));
     h->info = &info;
     h->functions = &functions;
+    h->params = NULL;
+    h->seq = 0;
     return(h);
 }
 
--- a/libmpdemux/tvi_dummy.c	Sat Nov 10 23:28:10 2001 +0000
+++ b/libmpdemux/tvi_dummy.c	Sat Nov 10 23:32:10 2001 +0000
@@ -1,58 +1,92 @@
-#include <stdio.h>
+/*
+    Only a sample!
+*/
 
 #include "config.h"
 
 #ifdef USE_TV
+
+#include <stdio.h>
 #include "tv.h"
 
+/* information about this file */
 static tvi_info_t info = {
 	"NULL-TV",
 	"dummy",
 	"alex",
-	"non-completed"
+	NULL
 };
 
+/* private data's */
 typedef struct {
+    int width;
+    int height;
 } priv_t;
 
 #include "tvi_def.h"
 
+/* handler creator - entry point ! */
 tvi_handle_t *tvi_init_dummy(char *device)
 {
     return new_handle();
 }
 
-static int init(priv_t *priv)
+/* initialisation */
+static int init(priv_t *priv, tvi_param_t *params)
+{
+    return 1;
+}
+
+/* that's the real start, we'got the format parameters (checked with control) */
+static int start(priv_t *priv)
 {
 }
 
-static int close(priv_t *priv)
+static int uninit(priv_t *priv)
 {
 }
 
 static int control(priv_t *priv, int cmd, void *arg)
 {
+    switch(cmd)
+    {
+	case TVI_CONTROL_IS_VIDEO:
+	    return(TVI_CONTROL_TRUE);
+	case TVI_CONTROL_VID_GET_FORMAT:
+	    (int)*(void **)arg = IMGFMT_YV12;
+	    return(TVI_CONTROL_TRUE);
+	case TVI_CONTROL_VID_SET_WIDTH:
+	    priv->width = (int)*(void **)arg;
+	    return(TVI_CONTROL_TRUE);
+	case TVI_CONTROL_VID_SET_HEIGHT:
+	    priv->height = (int)*(void **)arg;
+	    return(TVI_CONTROL_TRUE);	    
+	case TVI_CONTROL_VID_CHK_WIDTH:
+	case TVI_CONTROL_VID_CHK_HEIGHT:
+	    return(TVI_CONTROL_TRUE);
+    }
     return(TVI_CONTROL_UNKNOWN);
 }
 
 static int grab_video_frame(priv_t *priv, char *buffer, int len)
 {
-    memset(buffer, 0x77, len);
+    memset(buffer, 0x42, len);
 }
 
 static int get_video_framesize(priv_t *priv)
 {
-    return 0;
+    /* YV12 */
+    return priv->width*priv->height*12/8;
 }
 
 static int grab_audio_frame(priv_t *priv, char *buffer, int len)
 {
-    memset(buffer, 0x77, len);
+    memset(buffer, 0x42, len);
 }
 
 static int get_audio_framesize(priv_t *priv)
 {
-    return 0;
+    return 1;
 }
 
 #endif /* USE_TV */
--- a/libmpdemux/tvi_v4l.c	Sat Nov 10 23:28:10 2001 +0000
+++ b/libmpdemux/tvi_v4l.c	Sat Nov 10 23:32:10 2001 +0000
@@ -1,3 +1,14 @@
+/*
+  v4l interface for libmpemux/tvi
+
+  (C) Alex Beregszaszi <alex@naxine.org>
+  
+  Some ideas are based on xawtv/libng's grab-v4l.c written by
+    Gerd Knorr <kraxel@bytesex.org>
+
+  CODE IS UNDER DEVELOPMENT, NO FEATURE REQUESTS PLEASE!
+*/
+
 #include "config.h"
 
 #ifdef USE_TV
@@ -5,6 +16,7 @@
 #include <stdio.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <signal.h>
 #include <sys/ioctl.h>
 #include <sys/types.h>
 #include <linux/videodev.h>
@@ -16,35 +28,168 @@
 static tvi_info_t info = {
 	"Video for Linux TV Input",
 	"v4l",
-	"alex",
-	"non-completed"
+	"Alex Beregszaszi <alex@naxine.org>",
+	"under development"
+};
+
+struct vid_fmt {
+    int fmtid;
+    int width;
+    int height;
+    int bytesperline;
 };
 
 typedef struct {
+    /* general */
     char			*video_device;
     int				fd;
     struct video_capability	capability;
     struct video_channel	*channels;
     struct video_tuner		tuner;
-    struct video_audio		audio;
+
+    /* video */
     struct video_picture	picture;
-
-    int				buffered;
-    struct video_mbuf		mbuf;
-    unsigned int		*mmap;
-    struct video_mmap		*buf;
-    
+    int				format;		/* output format */
     int				width;
     int				height;
+    int				bytesperline;
+
+    struct video_mbuf		mbuf;
+    unsigned char		*mmap;
+    struct video_mmap		*buf;
+    int				nbuf;
+    int				queue;
+
+    /* audio */
+    struct video_audio		audio;
 } priv_t;
 
 #include "tvi_def.h"
 
 static const char *device_cap[] = {
     "capture", "tuner", "teletext", "overlay", "chromakey", "clipping",
-    "frameram", "scales", "monochrome", NULL
+    "frameram", "scales", "monochrome", "subcapture", "mpeg-decoder",
+    "mpeg-encoder", "mjpeg-decoder", "mjpeg-encoder", NULL
+};
+
+static const char *device_pal[] = {
+    "-", "grey", "hi240", "rgb16", "rgb24", "rgb32", "rgb15", "yuv422",
+    "yuyv", "uyvy", "yuv420", "yuv411", "raw", "yuv422p", "yuv411p",
+    "yuv420p", "yuv410p", NULL
+};
+#define PALETTE(x) ((x < sizeof(device_pal)/sizeof(char*)) ? device_pal[x] : "UNKNOWN")
+
+static int palette2depth(int palette)
+{
+    if (palette == VIDEO_PALETTE_YUV420P)
+	return 12;
+    return 32;
+}
+
+static int format2palette(int format)
+{
+    if (format == IMGFMT_YV12)
+	return VIDEO_PALETTE_YUV420P;
+    return VIDEO_PALETTE_RGB24;
+}
+
+#if 0
+struct STRTAB {
+    long	nr;
+    const char	*str;
+};
+
+static struct STRTAB stereo[] = {
+    { 0,			"auto"		},
+    { VIDEO_SOUND_MONO,		"mono"		},
+    { VIDEO_SOUND_STEREO,	"stereo"	},
+    { VIDEO_SOUND_LANG1,	"lang1"		},
+    { VIDEO_SOUND_LANG2,	"lang1"		},
+    { -1,			NULL		}    
+};
+
+static struct STRTAB norms_v4l[] = {
+    { VIDEO_MODE_PAL,		"PAL"		},
+    { VIDEO_MODE_NTSC,		"NTSC"		},
+    { VIDEO_MODE_SECAM,		"SECAM"		},
+    { VIDEO_MODE_AUTO,		"AUTO"		},
+    { -1,			NULL		}    
+};
+
+static struct STRTAB norms_bttv[] = {
+    { VIDEO_MODE_PAL,		"PAL"		},
+    { VIDEO_MODE_NTSC,		"NTSC"		},
+    { VIDEO_MODE_SECAM,		"SECAM"		},
+    { 3,			"PAL-NC"	},
+    { 4,			"PAL-M"		},
+    { 5,			"PAL-N"		},
+    { 6,			"NTSC-JP"	},
+    { -1,			NULL		}    
 };
 
+static unsigned short _format2palette[VIDEO_FMT_COUNT] = {
+    0,				/* unused */
+    VIDEO_PALETTE_HI240,	/* RGB8 */
+    VIDEO_PALETTE_GREY,
+    VIDEO_PALETTE_RGB555,
+    VIDEO_PALETTE_RGB565,
+    0,
+    0,
+    VIDEO_PALETTE_RGB24,
+    VIDEO_PALETTE_RGB32,
+    0,
+    0,
+    0,
+    0,
+    VIDEO_PALETTE_YUV422,
+    VIDEO_PALETTE_YUV422P,
+    VIDEO_PALETTE_YUV420P,
+};
+#define FMT2PAL(fmt) ((fmt < sizeof(format2palette)/sizeof(unsigned short)) ? \
+			format2palette[fmt] : 0);
+
+const unsigned int vfmt_to_depth[] = {
+    0,
+    8,
+    8,
+    16,
+    16,
+    16,
+    16,
+    24,
+    32,
+    24,
+    32,
+    16,
+    32,
+    16,
+    16,
+    12,
+    0,
+    0,
+};
+#endif
+
+static int one = 1, zero = 0;
+
+static int alarms;
+
+static void sigalarm(int signal)
+{
+    alarms++;
+    printf("v4l: timeout (got SIGALRM), hardware/driver problems?\n");
+}
+
+static void siginit(void)
+{
+    struct sigaction act, old;
+    
+    memset(&act, 0, sizeof(act));
+    act.sa_handler = sigalarm;
+    sigemptyset(&act.sa_mask);
+    sigaction(SIGALRM, &act, &old);
+}
+
 tvi_handle_t *tvi_init_v4l(char *device)
 {
     tvi_handle_t *h;
@@ -56,10 +201,11 @@
 
     priv = h->priv;
 
+    /* set video device name */
     if (!device)
     {
 	priv->video_device = malloc(strlen("/dev/video0"));
-	strcpy(priv->video_device, &"/dev/video0");
+	sprintf(priv->video_device, "/dev/video0");
     }
     else
     {
@@ -70,40 +216,52 @@
     return(h);
 }
 
-static int init(priv_t *priv)
+static int init(priv_t *priv, tvi_param_t *params)
 {
     int i;
 
     priv->fd = open(priv->video_device, O_RDONLY);
     if (priv->fd == -1)
     {
-	printf("v4l: open %s: %s\n", priv->video_device, strerror(errno));
+	printf("v4l: unable to open '%s': %s\n",
+	    priv->video_device, strerror(errno));
 	goto err;
     }
 
     printf("fd: %d\n", priv->fd);
     
-    /* get capabilities */
+    /* get capabilities (priv->capability is needed!) */
     if (ioctl(priv->fd, VIDIOCGCAP, &priv->capability) == -1)
     {
-	printf("v4l: ioctl error: %s\n", strerror(errno));
+	printf("ioctl get capabilites error: %s\n", strerror(errno));
 	goto err;
     }
 
     fcntl(priv->fd, F_SETFD, FD_CLOEXEC);
-    
-    printf("capabilites: ");
+    siginit();
+
+#if 0
+    for (i=0; params[i].opt; i++)
+    {
+	if (!strcmp(params[i].opt, "input"))
+	    priv->input = (int)*(void **)params[i].value;
+    }
+    printf("priv->input: %d\n", priv->input);
+#endif
+
+    printf("Selected device: %s\n", priv->capability.name);    
+    printf(" Capabilites: ");
     for (i = 0; device_cap[i] != NULL; i++)
 	if (priv->capability.type & (1 << i))
-	    printf(" %s", device_cap[i]);
+	    printf("%s ", device_cap[i]);
     printf("\n");
-    printf(" type: %d\n", priv->capability.type);
-    printf(" size: %dx%d => %dx%d\n",
+    printf(" Device type: %d\n", priv->capability.type);
+    printf(" Supported sizes: %dx%d => %dx%d\n",
 	priv->capability.minwidth, priv->capability.minheight,
 	priv->capability.maxwidth, priv->capability.maxheight);
     priv->width = priv->capability.minwidth;
     priv->height = priv->capability.minheight;
-    printf(" channels: %d\n", priv->capability.channels);
+    printf(" Inputs: %d\n", priv->capability.channels);
 
     priv->channels = malloc(sizeof(struct video_channel)*priv->capability.channels);
     memset(priv->channels, 0, sizeof(struct video_channel)*priv->capability.channels);
@@ -111,40 +269,51 @@
     {
 	priv->channels[i].channel = i;
 	ioctl(priv->fd, VIDIOCGCHAN, &priv->channels[i]);
-	printf(" %s: tuners:%d %s%s %s%s\n",
+	printf("  %d: %s: %s%s%s%s (tuner:%d, norm:%d)\n", i,
 	    priv->channels[i].name,
-	    priv->channels[i].tuners,
 	    (priv->channels[i].flags & VIDEO_VC_TUNER) ? "tuner " : "",
 	    (priv->channels[i].flags & VIDEO_VC_AUDIO) ? "audio " : "",
 	    (priv->channels[i].flags & VIDEO_TYPE_TV) ? "tv " : "",
-	    (priv->channels[i].flags & VIDEO_TYPE_CAMERA) ? "camera " : "");
+	    (priv->channels[i].flags & VIDEO_TYPE_CAMERA) ? "camera " : "",
+	    priv->channels[i].tuners,
+	    priv->channels[i].norm);
+    }
+
+    if (!(priv->capability.type & VID_TYPE_CAPTURE))
+    {
+	printf("Only grabbing supported (for overlay use another program)\n");
+	goto err;
+    }
+    
+    /* map grab buffer */
+    if (ioctl(priv->fd, VIDIOCGMBUF, &priv->mbuf) == -1)
+    {
+	printf("ioctl get mbuf failed: %s\n", strerror(errno));
+	goto err;
     }
 
-    if (priv->capability.type & VID_TYPE_CAPTURE)
+    printf("mbuf: size=%d, frames=%d\n",
+	priv->mbuf.size, priv->mbuf.frames);
+    priv->mmap = mmap(0, priv->mbuf.size, PROT_READ|PROT_WRITE,
+		MAP_SHARED, priv->fd, 0);
+    if (priv->mmap == -1)
+    {
+	printf("Unabel to map memory for buffers: %s\n", strerror(errno));
+        priv->mmap = malloc(priv->mbuf.size); /* our buffer */
+    }
+    if (!priv->mmap)
     {
-	if (ioctl(priv->fd, VIDIOCGMBUF, &priv->mbuf) == 0)
-	{
-	    printf("mbuf: size=%d, frames=%d (first offset: %p)\n",
-		priv->mbuf.size, priv->mbuf.frames, priv->mbuf.offsets[0]);
-	    priv->mmap = mmap(0, priv->mbuf.size, PROT_READ|PROT_WRITE,
-			    MAP_SHARED, priv->fd, 0);
-	    if (priv->mmap == -1)
-		perror("mmap");
-	}
-	else
-	    priv->mmap = -1;
+	printf("Unable to allocate memory for buffers: %s\n", strerror(errno));
+	goto err;
+    }
+    printf("our buffer: %p\n", priv->mmap);
 
-	if (priv->mmap != -1)
-	{
-	    priv->buf = malloc(priv->mbuf.frames * sizeof(struct video_mmap));
-	    memset(priv->buf, 0, priv->mbuf.frames * sizeof(struct video_mmap));
-	    priv->buffered = 1;
-	}
-	else
-	    priv->buffered = 0;
-    }
+    /* num of buffers */
+    priv->nbuf = priv->mbuf.frames;
     
-    printf("buffered: %d\n", priv->buffered);
+    /* video buffers */
+    priv->buf = malloc(priv->nbuf * sizeof(struct video_mmap));
+    memset(priv->buf, 0, priv->nbuf * sizeof(struct video_mmap));
     
     return(1);
 
@@ -154,32 +323,119 @@
     return(0);
 }
 
-static int exit(priv_t *priv)
+static int uninit(priv_t *priv)
 {
+#warning "Implement uninit!"
 }
 
-static int tune(priv_t *priv, int freq, int chan, int norm)
+static int start(priv_t *priv)
 {
-    if (freq)
+    int i;
+
+    
+    if (ioctl(priv->fd, VIDIOCGPICT, &priv->picture) == -1)
     {
-	ioctl(priv->fd, VIDIOCSFREQ, &freq);
-	return(1);
+	printf("ioctl get picture failed: %s\n", strerror(errno));
+	return(0);
     }
 
-    if (chan && norm)
+    priv->picture.palette = format2palette(priv->format);
+    priv->picture.depth = palette2depth(priv->picture.palette);
+    priv->bytesperline = priv->width * priv->picture.depth / 8;
+    
+    printf("Picture values:\n");
+    printf(" Depth: %d, Palette: %d (Format: %s)\n", priv->picture.depth,
+	priv->picture.palette, vo_format_name(priv->format));
+    printf(" Brightness: %d, Hue: %d, Colour: %d, Contrast: %d\n",
+	priv->picture.brightness, priv->picture.hue,
+	priv->picture.colour, priv->picture.contrast);
+    
+
+    if (ioctl(priv->fd, VIDIOCSPICT, &priv->picture) == -1)
     {
-	/* set channel & norm ! */
+	printf("ioctl set picture failed: %s\n", strerror(errno));
+	return(0);
     }
 
-    return(0);
+    priv->nbuf = priv->mbuf.frames;
+    for (i=0; i < priv->nbuf; i++)
+    {
+	priv->buf[i].format = priv->picture.palette;
+	priv->buf[i].frame = i;
+	priv->buf[i].width = priv->width;
+	priv->buf[i].height = priv->height;
+	printf("buffer: %d => %p\n", i, &priv->buf[i]);
+    } 
+    
+    /* start capture */
+    if (ioctl(priv->fd, VIDIOCCAPTURE, &one) == -1)
+    {
+	printf("ioctl capture failed: %s\n", strerror(errno));
+	return(0);
+    }
 }
 
 static int control(priv_t *priv, int cmd, void *arg)
 {
+    printf("debug: control(priv=%p, cmd=%d, arg=%p)\n",
+	priv, cmd, arg);
     switch(cmd)
     {
+	/* ========== GENERIC controls =========== */
+	case TVI_CONTROL_IS_VIDEO:
+	{
+	    if (priv->capability.type & VID_TYPE_CAPTURE)
+		return(TVI_CONTROL_TRUE);
+	    return(TVI_CONTROL_FALSE);
+	}
+	case TVI_CONTROL_IS_AUDIO:
+	    return(TVI_CONTROL_FALSE);	/* IMPLEMENT CHECK! */
+	case TVI_CONTROL_IS_TUNER:
+	{
+	    if (priv->capability.type & VID_TYPE_TUNER)
+		return(TVI_CONTROL_TRUE);
+	    return(TVI_CONTROL_FALSE);
+	}
+
+	/* ========== VIDEO controls =========== */
 	case TVI_CONTROL_VID_GET_FORMAT:
-	    (int)*(void **)arg = 0x0;
+	{
+	    int output_fmt = -1;
+
+#if 0	    
+	    switch(priv->palette)
+	    {
+		case VIDEO_PALETTE_RGB555:
+		    output_fmt = IMGFMT_RGB15;
+		    break;
+		case VIDEO_PALETTE_RGB565:
+		    output_fmt = IMGFMT_RGB16;
+		    break;
+		case VIDEO_PALETTE_RGB24:
+		    output_fmt = IMGFMT_RGB24;
+		    break;
+		case VIDEO_PALETTE_RGB32:
+		    output_fmt = IMGFMT_RGB32;
+		    break;
+		case VIDEO_PALETTE_UYVY:
+		    output_fmt = IMGFMT_UYVY;
+		    break;
+		case VIDEO_PALETTE_YUV420P:
+		    output_fmt = IMGFMT_YV12;
+		    break;
+		default:
+		    printf("no suitable output format found (%s)\n",
+			PALETTE(priv->palette));
+		    return(TVI_CONTROL_FALSE);
+	    }
+#endif
+	    output_fmt = priv->format;
+	    (int)*(void **)arg = output_fmt;
+	    printf("Output format: %s\n", vo_format_name(output_fmt));
+	    return(TVI_CONTROL_TRUE);
+	}
+	case TVI_CONTROL_VID_SET_FORMAT:
+	    priv->format = (int)*(void **)arg;
 	    return(TVI_CONTROL_TRUE);
 	case TVI_CONTROL_VID_GET_PLANES:
 	    (int)*(void **)arg = 1;
@@ -194,7 +450,7 @@
 	{
 	    int req_width = (int)*(void **)arg;
 	    
-	    printf("req_width: %d\n", req_width);
+	    printf("Requested width: %d\n", req_width);
 	    if ((req_width > priv->capability.minwidth) &&
 		(req_width < priv->capability.maxwidth))
 		return(TVI_CONTROL_TRUE);
@@ -210,7 +466,7 @@
 	{
 	    int req_height = (int)*(void **)arg;
 	    
-	    printf("req_height: %d\n", req_height);
+	    printf("Requested height: %d\n", req_height);
 	    if ((req_height > priv->capability.minheight) &&
 		(req_height < priv->capability.maxheight))
 		return(TVI_CONTROL_TRUE);
@@ -220,14 +476,163 @@
 	    priv->height = (int)*(void **)arg;
 	    return(TVI_CONTROL_TRUE);
 
+	/* ========== TUNER controls =========== */
+	case TVI_CONTROL_TUN_GET_FREQ:
+	{
+	    unsigned long freq;
+	    
+	    if (ioctl(priv->fd, VIDIOCGFREQ, &freq) == -1)
+	    {
+		printf("ioctl get freq failed: %s\n", strerror(errno));
+		return(TVI_CONTROL_FALSE);
+	    }
+	    
+	    /* tuner uses khz not mhz ! */
+	    if (priv->tuner.flags & VIDEO_TUNER_LOW)
+	        freq /= 1000;
+	    (unsigned long)*(void **)arg = freq;
+	    return(TVI_CONTROL_TRUE);
+	}
 	case TVI_CONTROL_TUN_SET_FREQ:
 	{
-	    long freq = (long)*(void **)arg; /* shit: long -> freq */
+	    /* argument is in MHz ! */
+	    unsigned long freq = (unsigned long)*(void **)arg;
+	    
+	    printf("requested frequency: %lu MHz\n", (float)freq/16);
+	    
+	    /* tuner uses khz not mhz ! */
+	    if (priv->tuner.flags & VIDEO_TUNER_LOW)
+	        freq *= 1000;
+	    printf(" requesting from driver: freq=%.3f\n", (float)freq/16);
+	    if (ioctl(priv->fd, VIDIOCSFREQ, &freq) == -1)
+	    {
+		printf("ioctl set freq failed: %s\n", strerror(errno));
+		return(TVI_CONTROL_FALSE);
+	    }
+	    return(TVI_CONTROL_TRUE);
+	}
+	case TVI_CONTROL_TUN_GET_TUNER:
+	{
+	    if (ioctl(priv->fd, VIDIOCGTUNER, &priv->tuner) == -1)
+	    {
+		printf("ioctl get tuner failed: %s\n", strerror(errno));
+		return(TVI_CONTROL_FALSE);
+	    }
+	    
+	    printf("Tuner (%s) range: %lu -> %lu\n", priv->tuner.name,
+		priv->tuner.rangelow, priv->tuner.rangehigh);
+	    return(TVI_CONTROL_TRUE);
+	}
+	case TVI_CONTROL_TUN_SET_TUNER:
+	{
+	    if (ioctl(priv->fd, VIDIOCSTUNER, &priv->tuner) == -1)
+	    {
+		printf("ioctl get tuner failed: %s\n", strerror(errno));
+		return(TVI_CONTROL_FALSE);
+	    }
+	    return(TVI_CONTROL_TRUE);
+	}
+	case TVI_CONTROL_TUN_SET_NORM:
+	{
+	    int req_mode = (int)*(void **)arg;
+	    
+	    if ((!(priv->tuner.flags & VIDEO_TUNER_NORM)) ||
+		((req_mode == VIDEO_MODE_PAL) && !(priv->tuner.flags & VIDEO_TUNER_PAL)) ||
+		((req_mode == VIDEO_MODE_NTSC) && !(priv->tuner.flags & VIDEO_TUNER_NTSC)) ||
+		((req_mode == VIDEO_MODE_SECAM) && !(priv->tuner.flags & VIDEO_TUNER_SECAM)))
+	    {
+		printf("tuner isn't capable to set norm!\n");
+		return(TVI_CONTROL_FALSE);
+	    }
+
+	    priv->tuner.mode = req_mode;
 	    
-	    printf("requested frequency: %f\n", freq);
-	    if (ioctl(priv->fd, VIDIOCSFREQ, &freq) != -1)
-		return(TVI_CONTROL_TRUE);
-	    return(TVI_CONTROL_FALSE);
+	    if (control(priv->fd, TVI_CONTROL_TUN_SET_TUNER, &priv->tuner) != TVI_CONTROL_TRUE)
+		return(TVI_CONTROL_FALSE);
+	    return(TVI_CONTROL_TRUE);
+	}
+	case TVI_CONTROL_TUN_GET_NORM:
+	{
+	    (int)*(void **)arg = priv->tuner.mode;
+
+	    return(TVI_CONTROL_TRUE);
+	}
+	
+	/* ========== AUDIO controls =========== */
+	case TVI_CONTROL_AUD_GET_FORMAT:
+	{
+	    (int)*(void **)arg = AFMT_S16_LE;
+	    return(TVI_CONTROL_TRUE);
+	}
+	case TVI_CONTROL_AUD_GET_CHANNELS:
+	{
+	    (int)*(void **)arg = 2;
+	    return(TVI_CONTROL_TRUE);
+	}
+	case TVI_CONTROL_AUD_GET_SAMPLERATE:
+	{
+	    (int)*(void **)arg = 44100;
+	    return(TVI_CONTROL_TRUE);
+	}
+	case TVI_CONTROL_AUD_GET_SAMPLESIZE:
+	{
+	    (int)*(void **)arg = 76000;
+	    return(TVI_CONTROL_TRUE);
+	}
+	
+	/* ========== SPECIFIC controls =========== */
+	case TVI_CONTROL_SPC_GET_INPUT:
+	{
+	    int req_chan = (int)*(void **)arg;
+	    int i;
+
+	    for (i = 0; i < priv->capability.channels; i++)
+	    {
+		if (priv->channels[i].channel == req_chan)
+		    break;
+	    }
+
+	    if (ioctl(priv->fd, VIDIOCGCHAN, &priv->channels[i]) == -1)
+	    {
+		printf("ioctl get channel failed: %s\n", strerror(errno));
+		return(TVI_CONTROL_FALSE);
+	    }
+	    return(TVI_CONTROL_TRUE);
+	}
+
+	case TVI_CONTROL_SPC_SET_INPUT:
+	{
+	    struct video_channel chan;
+	    int req_chan = (int)*(void **)arg;
+	    int i;
+	    
+	    if (req_chan >= priv->capability.channels)
+	    {
+		printf("Invalid input requested: %d, valid: 0-%d\n",
+		    req_chan, priv->capability.channels);
+		return(TVI_CONTROL_FALSE);
+	    }
+
+	    for (i = 0; i < priv->capability.channels; i++)
+	    {
+		if (priv->channels[i].channel == req_chan)
+		    chan = priv->channels[i];
+	    }
+
+	    if (ioctl(priv->fd, VIDIOCSCHAN, &chan) == -1)
+	    {
+		printf("ioctl set chan failed: %s\n", strerror(errno));
+		return(TVI_CONTROL_FALSE);
+	    }
+	    printf("Using input '%s'\n", chan.name);
+
+	    /* update tuner state */
+	    if (priv->capability.type & VID_TYPE_TUNER)
+		control(priv, TVI_CONTROL_TUN_GET_TUNER, 0);
+
+	    /* update local channel list */	
+	    control(priv, TVI_CONTROL_SPC_GET_INPUT, &req_chan);
+	    return(TVI_CONTROL_TRUE);
 	}
     }
 
@@ -236,23 +641,33 @@
 
 static int grab_video_frame(priv_t *priv, char *buffer, int len)
 {
-    priv->buf[0].frame = 0;
-    priv->buf[0].width = 320;
-    priv->buf[0].height = 240;
-    priv->buf[0].format = VIDEO_PALETTE_YUV422;
-    
-    if (ioctl(priv->fd, VIDIOCMCAPTURE, priv->buf) == -1)
+    int frame = priv->queue % priv->nbuf;
+
+    printf("grab_video_frame(priv=%p, buffer=%p, len=%d\n",
+	priv, buffer, len);
+
+    printf("buf: %p + frame: %d => %p\n",
+	priv->buf, frame, &priv->buf[frame]);
+    if (ioctl(priv->fd, VIDIOCMCAPTURE, &priv->buf[frame]) == -1)
     {
-	printf("grab_video_frame failed: %s\n", strerror(errno));
-	return 0;
+	printf("ioctl mcapture failed: %s\n", strerror(errno));
+	return(0);
     }
     
-    return 1;
+    ioctl(priv->fd, VIDIOCSYNC, &priv->buf[frame]);
+    priv->queue++;
+    
+    printf("mmap: %p + offset: %d => %p\n",
+	priv->mmap, priv->mbuf.offsets[frame],
+	priv->mmap+priv->mbuf.offsets[frame]);
+    memcpy(buffer, priv->mmap+priv->mbuf.offsets[frame], len);
+
+    return(len);
 }
 
 static int get_video_framesize(priv_t *priv)
 {
-    return 65536;
+    return priv->bytesperline * priv->height;
 }
 
 static int grab_audio_frame(priv_t *priv, char *buffer, int len)