summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--cfg-common.h27
-rw-r--r--cfg-mencoder.h4
-rw-r--r--cfg-mplayer.h4
-rw-r--r--libmpdemux/Makefile2
-rw-r--r--libmpdemux/demuxer.c47
-rw-r--r--libmpdemux/demuxer.h1
-rw-r--r--libmpdemux/open.c18
-rw-r--r--libmpdemux/stream.c12
-rw-r--r--libmpdemux/stream.h1
-rw-r--r--libmpdemux/tv.c215
-rw-r--r--libmpdemux/tv.h62
-rw-r--r--libmpdemux/tvi_def.h44
-rw-r--r--libmpdemux/tvi_dummy.c58
-rw-r--r--libmpdemux/tvi_v4l.c267
14 files changed, 752 insertions, 10 deletions
diff --git a/cfg-common.h b/cfg-common.h
index b01a2350fc..1a0dba2c32 100644
--- a/cfg-common.h
+++ b/cfg-common.h
@@ -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
diff --git a/cfg-mencoder.h b/cfg-mencoder.h
index 0c8a9de8cb..e2717e3598 100644
--- a/cfg-mencoder.h
+++ b/cfg-mencoder.h
@@ -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 @@ struct config conf[]={
{"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},
diff --git a/cfg-mplayer.h b/cfg-mplayer.h
index c3f1dd1923..57b36c33a9 100644
--- a/cfg-mplayer.h
+++ b/cfg-mplayer.h
@@ -2,6 +2,8 @@
* config for cfgparser
*/
+#include "cfg-common.h"
+
extern char *playlist_file;
#ifdef HAVE_FBDEV
@@ -214,7 +216,9 @@ struct config conf[]={
{"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},
diff --git a/libmpdemux/Makefile b/libmpdemux/Makefile
index 43f0e3b512..9b3f058a59 100644
--- a/libmpdemux/Makefile
+++ b/libmpdemux/Makefile
@@ -3,7 +3,7 @@ LIBNAME = libmpdemux.a
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
diff --git a/libmpdemux/demuxer.c b/libmpdemux/demuxer.c
index be9c5e4938..76ee019e52 100644
--- a/libmpdemux/demuxer.c
+++ b/libmpdemux/demuxer.c
@@ -157,6 +157,9 @@ int demux_avi_fill_buffer_nini(demuxer_t *demux,demux_stream_t *ds);
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 @@ int demux_fill_buffer(demuxer_t *demux,demux_stream_t *ds){
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 @@ demuxer_t* demux_open_avi(demuxer_t* demuxer);
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 @@ sh_video_t *sh_video=NULL;
//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,14 +448,6 @@ if(file_format==DEMUXER_TYPE_MPEG_ES){ // little hack, see above!
mp_msg(MSGT_DEMUXER,MSGL_INFO,MSGTR_DetectedMPEGESfile);
}
}
-//=============== 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);
- if(vivo_check_file(demuxer)){
- mp_msg(MSGT_DEMUXER,MSGL_INFO,"Detected VIVO file format!\n");
- 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);
@@ -443,6 +456,14 @@ if(file_format==DEMUXER_TYPE_UNKNOWN || file_format==DEMUXER_TYPE_MOV){
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);
+ if(vivo_check_file(demuxer)){
+ mp_msg(MSGT_DEMUXER,MSGL_INFO,"Detected VIVO file format!\n");
+ file_format=DEMUXER_TYPE_VIVO;
+ }
+}
//=============== Unknown, exiting... ===========================
if(file_format==DEMUXER_TYPE_UNKNOWN){
mp_msg(MSGT_DEMUXER,MSGL_ERR,MSGTR_FormatNotRecognized);
@@ -531,6 +552,12 @@ switch(file_format){
}
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 @@ int demux_seek(demuxer_t *demuxer,float rel_seek_secs,int flags){
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;
diff --git a/libmpdemux/demuxer.h b/libmpdemux/demuxer.h
index 35008e253f..f07eb6602f 100644
--- a/libmpdemux/demuxer.h
+++ b/libmpdemux/demuxer.h
@@ -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
diff --git a/libmpdemux/open.c b/libmpdemux/open.c
index 22d16cf2cd..6b820697d4 100644
--- a/libmpdemux/open.c
+++ b/libmpdemux/open.c
@@ -70,6 +70,11 @@ typedef struct {
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 @@ if(dvd_title){
}
#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
diff --git a/libmpdemux/stream.c b/libmpdemux/stream.c
index 6f6f882c88..11f94cb29d 100644
--- a/libmpdemux/stream.c
+++ b/libmpdemux/stream.c
@@ -53,6 +53,13 @@ int stream_fill_buffer(stream_t *s){
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(newpos==0 || newpos!=s->pos){
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 {
diff --git a/libmpdemux/stream.h b/libmpdemux/stream.h
index 46246e2bcd..a6ed95c600 100644
--- a/libmpdemux/stream.h
+++ b/libmpdemux/stream.h
@@ -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
diff --git a/libmpdemux/tv.c b/libmpdemux/tv.c
new file mode 100644
index 0000000000..ed1377433c
--- /dev/null
+++ b/libmpdemux/tv.c
@@ -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 */
diff --git a/libmpdemux/tv.h b/libmpdemux/tv.h
new file mode 100644
index 0000000000..c9e7e6daa6
--- /dev/null
+++ b/libmpdemux/tv.h
@@ -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 */
diff --git a/libmpdemux/tvi_def.h b/libmpdemux/tvi_def.h
new file mode 100644
index 0000000000..9e9973b5f9
--- /dev/null
+++ b/libmpdemux/tvi_def.h
@@ -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);
+}
diff --git a/libmpdemux/tvi_dummy.c b/libmpdemux/tvi_dummy.c
new file mode 100644
index 0000000000..82d2c1e691
--- /dev/null
+++ b/libmpdemux/tvi_dummy.c
@@ -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 */
diff --git a/libmpdemux/tvi_v4l.c b/libmpdemux/tvi_v4l.c
new file mode 100644
index 0000000000..43fb8713f4
--- /dev/null
+++ b/libmpdemux/tvi_v4l.c
@@ -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 */