changeset 2790:98769cea155c

added tv subsystem
author alex
date Fri, 09 Nov 2001 23:46:06 +0000
parents 7023bf2ff439
children 8ef2ab5cd47f
files cfg-common.h cfg-mencoder.h cfg-mplayer.h libmpdemux/Makefile libmpdemux/demuxer.c libmpdemux/demuxer.h libmpdemux/open.c libmpdemux/stream.c libmpdemux/stream.h libmpdemux/tv.c libmpdemux/tv.h libmpdemux/tvi_def.h libmpdemux/tvi_dummy.c libmpdemux/tvi_v4l.c
diffstat 14 files changed, 752 insertions(+), 10 deletions(-) [+]
line wrap: on
line diff
--- a/cfg-common.h	Fri Nov 09 22:20:59 2001 +0000
+++ b/cfg-common.h	Fri Nov 09 23:46:06 2001 +0000
@@ -1,4 +1,4 @@
-
+#ifdef MAIN_CONF /* this will be included in conf[] */
 // ------------------------- stream options --------------------
 
 #ifdef USE_STREAM_CACHE
@@ -79,3 +79,28 @@
         {"oldpp", "MPlayer was compiled without opendivx library", CONF_TYPE_PRINT, CONF_NOCFG, 0, 0},
 #endif
 
+#ifdef USE_TV
+	{"tv", tvopts_conf, CONF_TYPE_SUBCONFIG, 0, 0, 0},
+#endif
+
+#else
+
+#include "config.h"
+
+#ifdef USE_TV
+#include "libmpdemux/tv.h"
+
+struct config tvopts_conf[]={
+	{"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},
+	{"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},
+	{NULL, NULL, 0, 0, 0, 0}
+};
+#endif
+
+#endif
--- a/cfg-mencoder.h	Fri Nov 09 22:20:59 2001 +0000
+++ b/cfg-mencoder.h	Fri Nov 09 23:46:06 2001 +0000
@@ -2,6 +2,8 @@
  * config for cfgparser
  */
 
+#include "cfg-common.h"
+
 #ifdef USE_FAKE_MONO
 extern int fakemono; // defined in dec_audio.c
 #endif
@@ -71,7 +73,9 @@
 	{"divx4opts", divx4opts_conf, CONF_TYPE_SUBCONFIG, 0, 0, 0},
 	{"lameopts", lameopts_conf, CONF_TYPE_SUBCONFIG, 0, 0, 0},
 
+#define MAIN_CONF
 #include "cfg-common.h"
+#undef MAIN_CONF
 
 //	{"quiet", &quiet, CONF_TYPE_FLAG, 0, 0, 1},
 	{"verbose", &verbose, CONF_TYPE_INT, CONF_RANGE, 0, 100},
--- a/cfg-mplayer.h	Fri Nov 09 22:20:59 2001 +0000
+++ b/cfg-mplayer.h	Fri Nov 09 23:46:06 2001 +0000
@@ -2,6 +2,8 @@
  * config for cfgparser
  */
 
+#include "cfg-common.h"
+
 extern char *playlist_file;
 
 #ifdef HAVE_FBDEV
@@ -214,7 +216,9 @@
 	{"skin", &skinName, CONF_TYPE_STRING, 0, 0, 0},
 #endif
 
+#define MAIN_CONF
 #include "cfg-common.h"
+#undef MAIN_CONF
         
 	{"quiet", &quiet, CONF_TYPE_FLAG, 0, 0, 1},
 	{"verbose", &verbose, CONF_TYPE_INT, CONF_RANGE, 0, 100},
--- a/libmpdemux/Makefile	Fri Nov 09 22:20:59 2001 +0000
+++ b/libmpdemux/Makefile	Fri Nov 09 23:46:06 2001 +0000
@@ -3,7 +3,7 @@
 
 include ../config.mak
 
-SRCS = mp3_hdr.c video.c mpeg_hdr.c cache2.c asfheader.c aviheader.c aviprint.c aviwrite.c demux_asf.c demux_avi.c demux_mov.c demux_mpg.c demux_viv.c demuxer.c dvdauth.c open.c parse_es.c stream.c
+SRCS = mp3_hdr.c video.c mpeg_hdr.c cache2.c asfheader.c aviheader.c aviprint.c aviwrite.c demux_asf.c demux_avi.c demux_mov.c demux_mpg.c demux_viv.c demuxer.c dvdauth.c open.c parse_es.c stream.c tv.c tvi_dummy.c tvi_v4l.c
 ifeq ($(STREAMING),yes)
 SRCS += asf_streaming.c url.c http.c network.c
 endif
--- a/libmpdemux/demuxer.c	Fri Nov 09 22:20:59 2001 +0000
+++ b/libmpdemux/demuxer.c	Fri Nov 09 23:46:06 2001 +0000
@@ -157,6 +157,9 @@
 int demux_asf_fill_buffer(demuxer_t *demux);
 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);
+#endif
 
 int demux_fill_buffer(demuxer_t *demux,demux_stream_t *ds){
   // Note: parameter 'ds' can be NULL!
@@ -170,6 +173,9 @@
     case DEMUXER_TYPE_ASF: return demux_asf_fill_buffer(demux);
     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);
+#endif
   }
   return 0;
 }
@@ -344,6 +350,10 @@
 int mov_check_file(demuxer_t* demuxer);
 int mov_read_header(demuxer_t* demuxer);
 
+#ifdef USE_TV
+/* tv ! */
+extern int tv_param_on;
+#endif
 
 demuxer_t* demux_open(stream_t *stream,int file_format,int audio_id,int video_id,int dvdsub_id){
 
@@ -359,6 +369,17 @@
 
 //printf("demux_open(%p,%d,%d,%d,%d)  \n",stream,file_format,audio_id,video_id,dvdsub_id);
 
+#ifdef USE_TV
+//=============== Try to open as TV-input: =================
+if(file_format==DEMUXER_TYPE_UNKNOWN || file_format==DEMUXER_TYPE_TV){
+  demuxer=new_demuxer(stream,DEMUXER_TYPE_TV,audio_id,video_id,dvdsub_id);
+  if(tv_param_on==1)
+  {
+    mp_msg(MSGT_DEMUXER,MSGL_INFO,"Detected TV! ;-)\n");
+    file_format=DEMUXER_TYPE_TV;
+  }
+}
+#endif
 //=============== Try to open as AVI file: =================
 if(file_format==DEMUXER_TYPE_UNKNOWN || file_format==DEMUXER_TYPE_AVI){
   demuxer=new_demuxer(stream,DEMUXER_TYPE_AVI,audio_id,video_id,dvdsub_id);
@@ -427,6 +448,14 @@
     mp_msg(MSGT_DEMUXER,MSGL_INFO,MSGTR_DetectedMPEGESfile);
   }
 }
+//=============== Try to open as MOV file: =================
+if(file_format==DEMUXER_TYPE_UNKNOWN || file_format==DEMUXER_TYPE_MOV){
+  demuxer=new_demuxer(stream,DEMUXER_TYPE_MOV,audio_id,video_id,dvdsub_id);
+  if(mov_check_file(demuxer)){
+      mp_msg(MSGT_DEMUXER,MSGL_INFO,MSGTR_DetectedQTMOVfile);
+      file_format=DEMUXER_TYPE_MOV;
+  }
+}
 //=============== Try to open as VIVO file: =================
 if(file_format==DEMUXER_TYPE_UNKNOWN || file_format==DEMUXER_TYPE_VIVO){
   demuxer=new_demuxer(stream,DEMUXER_TYPE_VIVO,audio_id,video_id,dvdsub_id);
@@ -435,14 +464,6 @@
       file_format=DEMUXER_TYPE_VIVO;
   }
 }
-//=============== Try to open as MOV file: =================
-if(file_format==DEMUXER_TYPE_UNKNOWN || file_format==DEMUXER_TYPE_MOV){
-  demuxer=new_demuxer(stream,DEMUXER_TYPE_MOV,audio_id,video_id,dvdsub_id);
-  if(mov_check_file(demuxer)){
-      mp_msg(MSGT_DEMUXER,MSGL_INFO,MSGTR_DetectedQTMOVfile);
-      file_format=DEMUXER_TYPE_MOV;
-  }
-}
 //=============== Unknown, exiting... ===========================
 if(file_format==DEMUXER_TYPE_UNKNOWN){
   mp_msg(MSGT_DEMUXER,MSGL_ERR,MSGTR_FormatNotRecognized);
@@ -531,6 +552,12 @@
   }
   break;
  }
+#ifdef USE_TV
+ case DEMUXER_TYPE_TV: {
+    demux_open_tv(demuxer);
+    break;
+ }
+#endif
 } // switch(file_format)
 pts_from_bps=0; // !!!
 return demuxer;
@@ -550,6 +577,10 @@
 if(!demuxer->seekable){
     if(demuxer->file_format==DEMUXER_TYPE_AVI)
 	mp_msg(MSGT_SEEK,MSGL_WARN,MSGTR_CantSeekRawAVI);
+#ifdef USE_TV
+    else if (demuxer->file_format==DEMUXER_TYPE_TV)
+	mp_msg(MSGT_SEEK,MSGL_WARN,"TV input isn't seekable! (probarly seeking will be for changing channels ;)\n");
+#endif
     else
 	mp_msg(MSGT_SEEK,MSGL_WARN,MSGTR_CantSeekFile);
     return 0;
--- a/libmpdemux/demuxer.h	Fri Nov 09 22:20:59 2001 +0000
+++ b/libmpdemux/demuxer.h	Fri Nov 09 23:46:06 2001 +0000
@@ -11,6 +11,7 @@
 #define DEMUXER_TYPE_ASF 6
 #define DEMUXER_TYPE_MOV 7
 #define DEMUXER_TYPE_VIVO 8
+#define DEMUXER_TYPE_TV 9
 
 #define DEMUXER_TIME_NONE 0
 #define DEMUXER_TIME_PTS 1
--- a/libmpdemux/open.c	Fri Nov 09 22:20:59 2001 +0000
+++ b/libmpdemux/open.c	Fri Nov 09 23:46:06 2001 +0000
@@ -70,6 +70,11 @@
 
 extern int vcd_get_track_end(int fd,int track);
 
+#ifdef USE_TV
+#include "tv.h"
+extern tvi_handle_t *tv_handler;
+#endif
+
 // Open a new stream  (stdin/file/vcd/url)
 
 stream_t* open_stream(char* filename,int vcd_track,int* file_format){
@@ -242,6 +247,19 @@
 }
 #endif
 
+#ifdef USE_TV
+//============ Check for TV-input ====
+  if (tv_param_on==1)
+  {
+    stream=new_stream(-1,STREAMTYPE_TV);
+    tv_handler = tv_begin();
+    if (!tv_handler)
+	return(NULL);
+    tv_init(tv_handler);
+    return(stream);
+  }
+#endif
+
 //============ Open STDIN ============
   if(!strcmp(filename,"-")){
       // read from stdin
--- a/libmpdemux/stream.c	Fri Nov 09 22:20:59 2001 +0000
+++ b/libmpdemux/stream.c	Fri Nov 09 23:46:06 2001 +0000
@@ -53,6 +53,13 @@
     break;
   }
 #endif
+#ifdef USE_TV
+  case STREAMTYPE_TV:
+  {
+    len = 0;
+    break;
+  }
+#endif
   default: len=0;
   }
   if(len<=0){ s->eof=1; s->buf_pos=s->buf_len=0; return 0; }
@@ -126,6 +133,11 @@
       if(stream_fill_buffer(s)<=0) break; // EOF
     }
     break;
+#ifdef USE_TV
+  case STREAMTYPE_TV:
+    s->pos=newpos; /* no sense */
+    break;
+#endif
   }
 //   putchar('.');fflush(stdout);
 //} else {
--- a/libmpdemux/stream.h	Fri Nov 09 22:20:59 2001 +0000
+++ b/libmpdemux/stream.h	Fri Nov 09 23:46:06 2001 +0000
@@ -8,6 +8,7 @@
 #define STREAMTYPE_STREAM 2    // same as FILE but no seeking (for stdin)
 #define STREAMTYPE_DVD  3
 #define STREAMTYPE_MEMORY  4
+#define STREAMTYPE_TV	5
 
 #define VCD_SECTOR_SIZE 2352
 #define VCD_SECTOR_OFFS 24
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmpdemux/tv.c	Fri Nov 09 23:46:06 2001 +0000
@@ -0,0 +1,215 @@
+/*
+ TV subsystem for libMPDemux by Alex
+ 
+ API idea based on libvo2's
+
+ UNDER HEAVY DEVELOPEMENT, DO NOT USE! :)
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#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;
+
+/* some default values */
+float tv_param_freq = 0.0;
+char *tv_param_channel = "0";
+char *tv_param_norm = "pal";
+int tv_param_on = 0;
+char *tv_param_device = NULL;
+char *tv_param_driver = "dummy";
+int tv_param_width = -1;
+int tv_param_height = -1;
+
+
+/* ================== DEMUX_TV ===================== */
+/*
+  Return value:
+    0 = EOF(?) or no stream
+    1 = successfully read a packet
+*/
+/* fill demux->video and demux->audio */
+int demux_tv_fill_buffer(demuxer_t *demux)
+{
+    int 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;
+
+    demux->filepos = -1;
+
+    /* ================== ADD VIDEO PACKET =================== */
+    len_video = tv_handler->functions->get_video_framesize(tv_handler->priv);
+    ds_video = demux->video;
+
+    if (!ds_video)
+    {    
+	dp_video = new_demux_packet(len_video);
+	tv_handler->functions->grab_video_frame(tv_handler->priv, dp_video->buffer, len_video);
+	dp_video->pos = demux->filepos;
+	ds_video->asf_packet = dp_video;
+	ds_video->asf_seq = seq;
+    }
+    else if (ds_video->asf_packet)
+    {
+	if (ds_video->asf_seq != seq)
+	{
+	    ds_add_packet(ds_video, ds_video->asf_packet);
+	    ds_video->asf_packet = NULL;
+	}
+	else
+	{
+	    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);
+	    mp_dbg(MSGT_DEMUX,MSGL_DBG4, "video data appended %d+%d\n", dp_video->len, len_video);
+	    dp_video->len += len_video;
+	}
+    }
+    
+
+    /* ================== ADD AUDIO PACKET =================== */
+    len_audio = tv_handler->functions->get_audio_framesize(tv_handler->priv);
+    ds_audio = demux->audio;
+    
+    if (!ds_audio)
+    {
+	dp_audio = new_demux_packet(len_audio);
+	tv_handler->functions->grab_audio_frame(tv_handler->priv, dp_audio->buffer, len_audio);
+	dp_audio->pos = demux->filepos;
+	ds_audio->asf_packet = dp_audio;
+	ds_audio->asf_seq = seq;
+    }
+    else if (ds_audio->asf_packet)
+    {
+	if (ds_audio->asf_seq != seq)
+	{
+	    ds_add_packet(ds_audio, ds_audio->asf_packet);
+	    ds_audio->asf_packet = NULL;
+	}
+	else
+	{
+	    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);
+	    mp_dbg(MSGT_DEMUX,MSGL_DBG4, "audio data appended %d+%d\n", dp_audio->len, len_audio);
+	    dp_audio->len += len_audio;
+	}
+    }
+
+    return 1;
+}
+
+int demux_open_tv(demuxer_t *demuxer)
+{
+    sh_video_t *sh_video;
+    tvi_handle_t *tvh = tv_handler;
+    tvi_functions_t *funcs = tvh->functions;
+    
+    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;
+
+    if(!sh_video->fps)
+    {
+	if (funcs->control(tvh->priv, TVI_CONTROL_VID_GET_FPS, &sh_video->fps) != TVI_CONTROL_TRUE)
+	    sh_video->fps = 24.0f;
+    }
+    sh_video->frametime = 1.0f/sh_video->fps;
+
+    /* set width */
+    if (tv_param_width != -1)
+    {
+	if (funcs->control(tvh->priv, TVI_CONTROL_VID_CHK_WIDTH, &tv_param_width) == TVI_CONTROL_TRUE)
+	{
+	    funcs->control(tvh->priv, TVI_CONTROL_VID_SET_WIDTH, &tv_param_width);
+	    sh_video->disp_w = tv_param_width;
+	}
+	else
+	{
+	    printf("Unable set requested width: %d\n", tv_param_width);
+	    funcs->control(tvh->priv, TVI_CONTROL_VID_GET_WIDTH, &sh_video->disp_w);
+	    tv_param_width = sh_video->disp_w;
+	}    
+    }
+    else
+	funcs->control(tvh->priv, TVI_CONTROL_VID_GET_WIDTH, &sh_video->disp_w);
+
+    /* set height */
+    if (tv_param_height != -1)
+    {
+	if (funcs->control(tvh->priv, TVI_CONTROL_VID_CHK_HEIGHT, &tv_param_height) == TVI_CONTROL_TRUE)
+	{
+	    funcs->control(tvh->priv, TVI_CONTROL_VID_SET_HEIGHT, &tv_param_height);
+	    sh_video->disp_h = tv_param_height;
+	}
+	else
+	{
+	    printf("Unable set requested height: %d\n", tv_param_height);
+	    funcs->control(tvh->priv, TVI_CONTROL_VID_GET_HEIGHT, &sh_video->disp_h);
+	    tv_param_height = sh_video->disp_h;
+	}    
+    }
+    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;
+    
+    demuxer->video->sh = sh_video;
+    sh_video->ds = demuxer->video;
+    demuxer->video->id = 0;
+
+    /* here comes audio init */
+}
+
+/* ================== STREAM_TV ===================== */
+tvi_handle_t *tv_begin()
+{
+    if (!strcmp(tv_param_driver, "dummy"))
+	return tvi_init_dummy(tv_param_device);
+    if (!strcmp(tv_param_driver, "v4l"))
+	return 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)
+{
+    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);
+
+    return tvi->functions->init(tvi->priv);
+}
+#endif /* USE_TV */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmpdemux/tv.h	Fri Nov 09 23:46:06 2001 +0000
@@ -0,0 +1,62 @@
+#include "config.h"
+
+#ifdef USE_TV
+extern float tv_param_freq;
+extern char *tv_param_channel;
+extern char *tv_param_norm;
+extern int tv_param_on;
+extern char *tv_param_device;
+extern char *tv_param_driver;
+extern int tv_param_width;
+extern int tv_param_height;
+
+typedef struct tvi_info_s
+{
+    const char *name;
+    const char *short_name;
+    const char *author;
+    const char *comment;
+} tvi_info_t;
+
+typedef struct tvi_functions_s
+{
+    int (*init)();
+    int (*exit)();
+    int (*control)();
+    int (*grab_video_frame)();
+    int (*get_video_framesize)();
+    int (*grab_audio_frame)();
+    int (*get_audio_framesize)();
+} tvi_functions_t;
+
+typedef struct tvi_handle_s {
+    tvi_info_t *info;
+    tvi_functions_t *functions;
+    void *priv;
+} tvi_handle_t;
+
+#define TVI_CONTROL_FALSE		0
+#define TVI_CONTROL_TRUE		1
+#define TVI_CONTROL_NA			-1
+#define TVI_CONTROL_UNKNOWN		-2
+
+
+#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
+
+#define TVI_CONTROL_TUN_GET_FREQ	100
+#define TVI_CONTROL_TUN_SET_FREQ	101
+
+#endif /* USE_TV */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmpdemux/tvi_def.h	Fri Nov 09 23:46:06 2001 +0000
@@ -0,0 +1,44 @@
+static int init(priv_t *priv);
+static int exit(priv_t *priv);
+static int control(priv_t *priv, int cmd, void *arg);
+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);
+static int get_audio_framesize(priv_t *priv);
+
+static tvi_functions_t functions =
+{
+    init,
+    exit,
+    control,
+    grab_video_frame,
+    get_video_framesize,
+    grab_audio_frame,
+    get_audio_framesize
+};
+
+static tvi_handle_t *new_handle()
+{
+    tvi_handle_t *h = malloc(sizeof(tvi_handle_t));
+
+    if (!h)
+	return(NULL);
+    h->priv = malloc(sizeof(priv_t));
+    if (!h->priv)
+    {
+	free(h);
+	return(NULL);
+    }
+    memset(h->priv, 0, sizeof(priv_t));
+    h->info = &info;
+    h->functions = &functions;
+    return(h);
+}
+
+static void free_handle(tvi_handle_t *h)
+{
+    if (h->priv)
+	free(h->priv);
+    if (h)
+	free(h);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmpdemux/tvi_dummy.c	Fri Nov 09 23:46:06 2001 +0000
@@ -0,0 +1,58 @@
+#include <stdio.h>
+
+#include "config.h"
+
+#ifdef USE_TV
+#include "tv.h"
+
+static tvi_info_t info = {
+	"NULL-TV",
+	"dummy",
+	"alex",
+	"non-completed"
+};
+
+typedef struct {
+} priv_t;
+
+#include "tvi_def.h"
+
+tvi_handle_t *tvi_init_dummy(char *device)
+{
+    return new_handle();
+}
+
+static int init(priv_t *priv)
+{
+}
+
+static int close(priv_t *priv)
+{
+}
+
+static int control(priv_t *priv, int cmd, void *arg)
+{
+    return(TVI_CONTROL_UNKNOWN);
+}
+
+static int grab_video_frame(priv_t *priv, char *buffer, int len)
+{
+    memset(buffer, 0x77, len);
+}
+
+static int get_video_framesize(priv_t *priv)
+{
+    return 0;
+}
+
+static int grab_audio_frame(priv_t *priv, char *buffer, int len)
+{
+    memset(buffer, 0x77, len);
+}
+
+static int get_audio_framesize(priv_t *priv)
+{
+    return 0;
+}
+
+#endif /* USE_TV */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmpdemux/tvi_v4l.c	Fri Nov 09 23:46:06 2001 +0000
@@ -0,0 +1,267 @@
+#include "config.h"
+
+#ifdef USE_TV
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <linux/videodev.h>
+#include <unistd.h>
+#include <sys/mman.h>
+
+#include "tv.h"
+
+static tvi_info_t info = {
+	"Video for Linux TV Input",
+	"v4l",
+	"alex",
+	"non-completed"
+};
+
+typedef struct {
+    char			*video_device;
+    int				fd;
+    struct video_capability	capability;
+    struct video_channel	*channels;
+    struct video_tuner		tuner;
+    struct video_audio		audio;
+    struct video_picture	picture;
+
+    int				buffered;
+    struct video_mbuf		mbuf;
+    unsigned int		*mmap;
+    struct video_mmap		*buf;
+    
+    int				width;
+    int				height;
+} priv_t;
+
+#include "tvi_def.h"
+
+static const char *device_cap[] = {
+    "capture", "tuner", "teletext", "overlay", "chromakey", "clipping",
+    "frameram", "scales", "monochrome", NULL
+};
+
+tvi_handle_t *tvi_init_v4l(char *device)
+{
+    tvi_handle_t *h;
+    priv_t *priv;
+    
+    h = new_handle();
+    if (!h)
+	return(NULL);
+
+    priv = h->priv;
+
+    if (!device)
+    {
+	priv->video_device = malloc(strlen("/dev/video0"));
+	strcpy(priv->video_device, &"/dev/video0");
+    }
+    else
+    {
+	priv->video_device = malloc(strlen(device));
+	strcpy(priv->video_device, device);
+    }
+
+    return(h);
+}
+
+static int init(priv_t *priv)
+{
+    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));
+	goto err;
+    }
+
+    printf("fd: %d\n", priv->fd);
+    
+    /* get capabilities */
+    if (ioctl(priv->fd, VIDIOCGCAP, &priv->capability) == -1)
+    {
+	printf("v4l: ioctl error: %s\n", strerror(errno));
+	goto err;
+    }
+
+    fcntl(priv->fd, F_SETFD, FD_CLOEXEC);
+    
+    printf("capabilites: ");
+    for (i = 0; device_cap[i] != NULL; i++)
+	if (priv->capability.type & (1 << i))
+	    printf(" %s", device_cap[i]);
+    printf("\n");
+    printf(" type: %d\n", priv->capability.type);
+    printf(" size: %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);
+
+    priv->channels = malloc(sizeof(struct video_channel)*priv->capability.channels);
+    memset(priv->channels, 0, sizeof(struct video_channel)*priv->capability.channels);
+    for (i = 0; i < priv->capability.channels; i++)
+    {
+	priv->channels[i].channel = i;
+	ioctl(priv->fd, VIDIOCGCHAN, &priv->channels[i]);
+	printf(" %s: tuners:%d %s%s %s%s\n",
+	    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 " : "");
+    }
+
+    if (priv->capability.type & VID_TYPE_CAPTURE)
+    {
+	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;
+
+	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;
+    }
+    
+    printf("buffered: %d\n", priv->buffered);
+    
+    return(1);
+
+err:
+    if (priv->fd != -1)
+	close(priv->fd);
+    return(0);
+}
+
+static int exit(priv_t *priv)
+{
+}
+
+static int tune(priv_t *priv, int freq, int chan, int norm)
+{
+    if (freq)
+    {
+	ioctl(priv->fd, VIDIOCSFREQ, &freq);
+	return(1);
+    }
+
+    if (chan && norm)
+    {
+	/* set channel & norm ! */
+    }
+
+    return(0);
+}
+
+static int control(priv_t *priv, int cmd, void *arg)
+{
+    switch(cmd)
+    {
+	case TVI_CONTROL_VID_GET_FORMAT:
+	    (int)*(void **)arg = 0x0;
+	    return(TVI_CONTROL_TRUE);
+	case TVI_CONTROL_VID_GET_PLANES:
+	    (int)*(void **)arg = 1;
+	    return(TVI_CONTROL_TRUE);
+	case TVI_CONTROL_VID_GET_BITS:
+	    (int)*(void **)arg = 12;
+	    return(TVI_CONTROL_TRUE);
+	case TVI_CONTROL_VID_GET_WIDTH:
+	    (int)*(void **)arg = priv->width;
+	    return(TVI_CONTROL_TRUE);
+	case TVI_CONTROL_VID_CHK_WIDTH:
+	{
+	    int req_width = (int)*(void **)arg;
+	    
+	    printf("req_width: %d\n", req_width);
+	    if ((req_width > priv->capability.minwidth) &&
+		(req_width < priv->capability.maxwidth))
+		return(TVI_CONTROL_TRUE);
+	    return(TVI_CONTROL_FALSE);
+	}
+	case TVI_CONTROL_VID_SET_WIDTH:
+	    priv->width = (int)*(void **)arg;
+	    return(TVI_CONTROL_TRUE);
+	case TVI_CONTROL_VID_GET_HEIGHT:
+	    (int)*(void **)arg = priv->height;
+	    return(TVI_CONTROL_TRUE);
+	case TVI_CONTROL_VID_CHK_HEIGHT:
+	{
+	    int req_height = (int)*(void **)arg;
+	    
+	    printf("req_height: %d\n", req_height);
+	    if ((req_height > priv->capability.minheight) &&
+		(req_height < priv->capability.maxheight))
+		return(TVI_CONTROL_TRUE);
+	    return(TVI_CONTROL_FALSE);
+	}
+	case TVI_CONTROL_VID_SET_HEIGHT:
+	    priv->height = (int)*(void **)arg;
+	    return(TVI_CONTROL_TRUE);
+
+	case TVI_CONTROL_TUN_SET_FREQ:
+	{
+	    long freq = (long)*(void **)arg; /* shit: long -> freq */
+	    
+	    printf("requested frequency: %f\n", freq);
+	    if (ioctl(priv->fd, VIDIOCSFREQ, &freq) != -1)
+		return(TVI_CONTROL_TRUE);
+	    return(TVI_CONTROL_FALSE);
+	}
+    }
+
+    return(TVI_CONTROL_UNKNOWN);
+}
+
+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)
+    {
+	printf("grab_video_frame failed: %s\n", strerror(errno));
+	return 0;
+    }
+    
+    return 1;
+}
+
+static int get_video_framesize(priv_t *priv)
+{
+    return 65536;
+}
+
+static int grab_audio_frame(priv_t *priv, char *buffer, int len)
+{
+}
+
+static int get_audio_framesize(priv_t *priv)
+{
+    return 65536;
+}
+
+#endif /* USE_TV */