--- configure	 (revision 22772)
+++ configure	 (working copy)
@@ -242,6 +242,7 @@
   --disable-tv-v4l1      disable Video4Linux TV interface [autodetect]
   --disable-tv-v4l2      disable Video4Linux2 TV interface [autodetect]
   --disable-tv-bsdbt848  disable BSD BT848 interface [autodetect]
+  --disable-tv-teletex   disable TV teletext interface [autodetect]
   --disable-pvr          disable Video4Linux2 MPEG PVR [autodetect]
   --disable-rtc          disable RTC (/dev/rtc) on Linux [autodetect]
   --disable-network      disable networking [enable]
@@ -585,6 +586,7 @@
 _tv_v4l1=auto
 _tv_v4l2=auto
 _tv_bsdbt848=auto
+_tv_teletext=auto
 _pvr=auto
 _network=yes
 _winsock2=auto
@@ -934,6 +936,8 @@
   --disable-tv-v4l1)	_tv_v4l1=no	;;
   --enable-tv-v4l2)	_tv_v4l2=yes	;;
   --disable-tv-v4l2)	_tv_v4l2=no	;;
+  --enable-tv-teletext)	_tv_teletext=yes		;;
+  --disable-tv-teletext)	_tv_teletext=no		;;
   --enable-radio)       _radio=yes	;;
   --enable-radio-capture)       _radio_capture=yes	;;
   --disable-radio-capture)       _radio_capture=no	;;
@@ -6752,6 +6756,28 @@
 fi
 echores "$_tv_v4l2"
 
+echocheck "TV teletext interface"
+if test "$_tv_teletext" = auto ; then
+ _tv_teletext=no
+ if test linux ; then
+  cat > $TMPC <<EOF
+#include <stdlib.h>
+#include <libzvbi.h>
+int main(void) { return 0; }
+EOF
+  cc_check && _tv_teletext=yes
+ fi
+fi
+if test "$_tv_teletext" = yes ; then
+  _def_tv_teletext='#define HAVE_TV_TELETEXT 1'
+  _ld_extra="$_ld_extra -lzvbi"
+  _inputmodules="tv-teletext $_inputmodules"
+else
+  _noinputmodules="tv-teletext $_noinputmodules"
+  _def_tv_teletext='#undef HAVE_TV_TELETEXT'
+fi
+echores "$_tv_teletext"
+
 
 echocheck "Radio interface"
 if test "$_radio" = yes ; then
@@ -7567,6 +7593,7 @@
 TV_V4L1 = $_tv_v4l1
 TV_V4L2 = $_tv_v4l2
 TV_BSDBT848 = $_tv_bsdbt848
+TV_TELETEXT = $_tv_teletext
 PVR = $_pvr
 VCD = $_vcd
 DVDREAD = $_dvdread
@@ -8093,6 +8120,9 @@
 /* Enable *BSD BrookTree TV interface support */
 $_def_tv_bsdbt848
 
+/* Enable TV Teletext Interface support */
+$_def_tv_teletext
+
 /* Enable Radio Interface support */
 $_def_radio
 
--- cfg-common.h	 (revision 22772)
+++ cfg-common.h	 (working copy)
@@ -460,6 +460,11 @@
 #endif
 	{"adevice", &tv_param_adevice, CONF_TYPE_STRING, 0, 0, 0, NULL},
 #endif
+#ifdef HAVE_TV_TELETEXT
+	{"tdevice", &tv_param_tdevice, CONF_TYPE_STRING, 0, 0, 0, NULL},
+	{"tformat", &tv_param_tformat, CONF_TYPE_STRING, 0, 0, 0, NULL},
+	{"tpage", &tv_param_tpage, CONF_TYPE_STRING, 0, 0, 0, NULL},
+#endif
 	{"audioid", &tv_param_audio_id, CONF_TYPE_INT, CONF_RANGE, 0, 9, NULL},
 	{NULL, NULL, 0, 0, 0, 0, NULL}
 };
--- mpcommon.c	 (revision 22772)
+++ mpcommon.c	 (working copy)
@@ -7,6 +7,11 @@
 #include "libvo/video_out.h"
 #include "spudec.h"
 #include "vobsub.h"
+#ifdef USE_TV
+#ifdef HAVE_TV_TELETEXT
+#include "stream/tv.h"
+#endif
+#endif
 
 double sub_last_pts = -303;
 
@@ -138,3 +143,28 @@
     }
     current_module=NULL;
 }
+
+void update_teletext(sh_video_t *sh_video, demuxer_t *demuxer, int reset)
+{
+#ifdef USE_TV
+#ifdef HAVE_TV_TELETEXT
+if (demuxer->type != DEMUXER_TYPE_TV) return;
+tvi_handle_t *tvh = demuxer->priv;
+if(!tvh) return;
+
+if(vo_spudec) {
+    tv_teletext_img_t* img=tv_get_teletext_imgpage(tvh);
+    if(img!=NULL) {
+	spudec_heartbeat_teletext(vo_spudec, img);
+	if(img->canvas) free(img->canvas);
+	free(img);
+	vo_osd_changed(OSDTYPE_SPU);
+	return;
+	}
+    vo_osd_changed(OSDTYPE_SPU);
+    }
+vo_osd_teletex_text=tv_get_teletext_txtpage(tvh);
+vo_osd_changed(OSDTYPE_TELETEXT);
+#endif
+#endif
+}
--- spudec.h	 (revision 22772)
+++ spudec.h	 (working copy)
@@ -50,5 +50,8 @@
 void spudec_calc_bbox(void *me, unsigned int dxs, unsigned int dys, unsigned int* bbox);
 void spudec_draw_scaled(void *me, unsigned int dxs, unsigned int dys, void (*draw_alpha)(int x0,int y0, int w,int h, int dp, unsigned char* src, unsigned char *srca, int stride));
 void spudec_set_forced_subs_only(void * const this, const unsigned int flag);
+#ifdef HAVE_TV_TELETEXT
+void spudec_heartbeat_teletext(void *this, void *imgptr);
+#endif
 #endif
 
--- spudec.c	 (revision 22772)
+++ spudec.c	 (working copy)
@@ -29,6 +29,9 @@
 #include "avutil.h"
 #endif
 #include "libswscale/swscale.h"
+#ifdef HAVE_TV_TELETEXT
+#include "stream/tv.h"
+#endif
 
 /* Valid values for spu_aamode:
    0: none (fastest, most ugly)
@@ -122,6 +125,7 @@
   unsigned int strideyuy;
 
   int dvdnav_color_spu;		/* flag: 0 = grayscale SPU, 1 = YUV color SPU 2 = RGB 3 = BGR */
+  int color_spu;		/* flag: 0 = grayscale SPU, 1 = YUV color SPU 2 = RGB 3 = BGR */
 
   int dvdnav_menu;		/* flag: 0=normal subtitle, 1=dvdnav menu */
   unsigned int dvdnav_sx;	/* dvdnav menu item box */
@@ -1557,6 +1561,7 @@
 spudec_handle_t *spu = (spudec_handle_t *)this;
 if (!spu) return;
 spu->dvdnav_menu=mode;
+spu->color_spu=cflg;
 if (mode)
     spu->dvdnav_color_spu=cflg;
     else
@@ -3289,3 +3294,324 @@
   spu->hw_spu = hw_spu;
   hw_spu->control(VOCTRL_SET_SPU_PALETTE,spu->global_palette);
 }
+
+#ifdef HAVE_TV_TELETEXT
+#define VBI_R(rgba) (((rgba) >> 0) & 0xFF)
+#define VBI_G(rgba) (((rgba) >> 8) & 0xFF)
+#define VBI_B(rgba) (((rgba) >> 16) & 0xFF)
+#define VBI_A(rgba) (((rgba) >> 24) & 0xFF)
+
+
+static unsigned char rgbtoy(int r, int g, int b) {
+int ret=(257*r+504*g+98*b+16000)/1000;
+return ret & 0xff;
+}
+
+static unsigned char rgbtou(int r, int g, int b) {
+int ret=(-148*r-291*g+429*b+128000)/1000;
+return ret & 0xff;
+}
+
+static unsigned char rgbtov(int r, int g, int b) {
+int ret=(439*r-368*g-71*b+128000)/1000;
+return ret & 0xff;
+}
+
+// correction u and v planes half size
+#define SPU_DOUBLE_SIZE	1
+
+void alloc_images(spudec_handle_t* spu, int cmode) {
+
+#if SPU_DOUBLE_SIZE
+if(cmode==0x03 && (spu->color_spu==DVDNAV_SPU_YUY || spu->color_spu==DVDNAV_SPU_YUV)) {
+    spu->start_col=spu->start_col*2;
+    spu->start_row=spu->start_row*2;
+    spu->end_col=spu->end_col*2;
+    spu->end_row=spu->end_row*2;
+    spu->width=spu->width*2;
+    spu->height=spu->height*2;
+    spu->orig_frame_width = spu->orig_frame_width*2;
+    spu->orig_frame_height = spu->orig_frame_height*2;
+    spu->stride = (spu->width+7)&(~7);
+    }
+#endif
+if (spu->image_size < spu->stride * spu->height) {
+    if (spu->image != NULL) {
+      free(spu->image);
+      spu->image_size = 0;
+    }
+    spu->image = malloc(2 * spu->stride * spu->height);
+if (spu->image) {
+      spu->image_size = spu->stride * spu->height;
+      spu->aimage = spu->image + spu->image_size;
+    }
+  }
+if(cmode!=0x03) return;
+if (spu->imageyuy) {
+    free(spu->imageyuy);
+    spu->imageyuy=NULL;
+    spu->aimageyuy=NULL;
+    }
+switch (spu->color_spu) {
+    case DVDNAV_SPU_YUY:
+    case DVDNAV_SPU_YUV:
+	spu->strideuv = spu->stride;
+	spu->start_coluv = spu->start_col/2;
+	spu->end_coluv = spu->end_col/2;
+	spu->start_rowuv = spu->start_row/2;
+	spu->end_rowuv = spu->end_row/2;
+	spu->heightuv = spu->height/2;
+	spu->widthuv = spu->width/2;
+	spu->strideuv = (spu->widthuv+7)&(~7);
+	if (spu->image_sizeuv < spu->strideuv * spu->heightuv) {
+	    if (spu->imageu != NULL) {
+	        free(spu->imageu);
+	        spu->image_sizeuv = 0; }
+	spu->imageu = malloc(3 * spu->strideuv * spu->heightuv);
+	if (spu->imageu) {
+	    spu->image_sizeuv = spu->strideuv * spu->heightuv;
+	    spu->imagev = spu->imageu + spu->image_sizeuv;
+	    spu->aimageuv = spu->imagev + spu->image_sizeuv; }
+    	    }
+	memset(spu->imageu,0,3 * spu->strideuv * spu->heightuv);
+	break;
+    case DVDNAV_SPU_RGB:
+    case DVDNAV_SPU_BGR:
+	spu->strideuv = spu->stride;
+	spu->start_coluv = spu->start_col;
+	spu->end_coluv = spu->end_col;
+	spu->start_rowuv = spu->start_row;
+	spu->end_rowuv = spu->end_row;
+	spu->heightuv = spu->height;
+	spu->widthuv = spu->width;
+	if (spu->image_sizeuv < spu->stride * spu->height) {
+	    if (spu->imageu != NULL) {
+	        free(spu->imageu);
+	        spu->image_sizeuv = 0; }
+	spu->imageu = malloc(2 * spu->stride * spu->height);
+	if (spu->imageu) {
+	    spu->image_sizeuv = spu->stride * spu->height;
+	    spu->imagev = spu->imageu + spu->image_sizeuv;
+	    spu->aimageuv = spu->imagev + spu->image_sizeuv; }
+	    }
+	memset(spu->imageu,0,2 * spu->stride * spu->height);
+	break;
+    }
+if(spu->imageu==NULL) {
+    free(spu->image);
+    spu->image=NULL;
+    spu->aimage=NULL;
+    spu->image_size = 0;
+    }
+return;
+}
+
+/*
+// Render from VBI_PIXFMT_RGBA32_LE to spu
+*/
+void spudec_heartbeat_teletext(void *this, void *imgptr)
+{
+int px,py;
+int grey,alpha,cy,cu,cv,alphauv;
+uint32_t *canvas;
+uint32_t *pin;
+spudec_handle_t *spu = (spudec_handle_t*)this;
+tv_teletext_img_t *img = (tv_teletext_img_t*)imgptr;
+unsigned char *iptr,*iptr2;
+unsigned char *aptr,*aptr2;
+unsigned char *uptr;
+unsigned char *vptr;
+unsigned char *auvptr;
+
+if(spu==NULL) return;
+if(img==NULL) return;
+if(img->canvas==NULL) {
+    spudec_reset(spu);
+    if (spu->image != NULL) free(spu->image);
+    spu->image=NULL;
+    spu->image_size = 0;
+    spu->dvdnav_color_spu=0;
+    spu->dvdnav_menu=0;
+    return;
+    }
+
+int h1 = 10;
+int hs = 0;
+if(img->half) h1=5;				// top half page
+if(img->half==2) hs=5;				// bottom half page
+
+spu->start_pts=0;
+spu->end_pts=0;
+spu->now_pts=1;
+spu->orig_frame_width = img->columns*12;	// 1 char width 12 pixel
+spu->orig_frame_height = img->rows*h1;		// 1 char height 10 pixel
+spu->scaled_frame_width = 0;
+spu->scaled_frame_height = 0;
+spu->start_col = 0;
+spu->end_col = img->columns*12;
+spu->start_row = 0;
+spu->end_row = img->rows*h1;
+spu->height = img->rows*h1;
+spu->width = img->columns*12;
+spu->height = (spu->height+3)&(~3); 		// round to 4
+spu->stride = (spu->width+7)&(~7); 		// round to 8
+
+alloc_images(spu,img->tformat);			// alloc images buffer
+if (spu->image == NULL) {
+    spudec_reset(spu);
+    return;
+    }
+canvas=img->canvas;				// RGBA32_LE image
+pin=canvas+(hs*img->columns*12*img->rows);
+
+spu->dvdnav_color_spu=0;
+spu->dvdnav_menu=0;
+if(img->tformat==0x03) spu->dvdnav_color_spu=spu->color_spu;
+switch(spu->dvdnav_color_spu) {
+    case DVDNAV_SPU_YUY:
+    case DVDNAV_SPU_YUV:
+#if SPU_DOUBLE_SIZE
+        for(py=0;py<img->rows*h1;py++) {
+    	    iptr=spu->image+py*spu->stride*2;	// image ptr pair line (Y plane)
+    	    iptr2=iptr+spu->stride;		// image ptr unpair line (Y plane)
+	    aptr=spu->aimage+py*spu->stride*2;	// image alpha ptr
+	    aptr2=aptr+spu->stride;
+	    uptr=spu->imageu+py*spu->strideuv;	// u planes ptr
+	    vptr=spu->imagev+py*spu->strideuv;	// v planes ptr
+	    auvptr=spu->aimageuv+py*spu->strideuv;	// au,av ptr
+	    for(px=0;px<img->columns*12;px++) {
+	        grey=rgbtoy(VBI_R(*pin),VBI_G(*pin),VBI_B(*pin));	// RGB to Y
+	        cu=rgbtou(VBI_R(*pin),VBI_G(*pin),VBI_B(*pin));		// RGB to U
+	        cv=rgbtov(VBI_R(*pin),VBI_G(*pin),VBI_B(*pin));		// RGB to V
+	        alpha=VBI_A(*pin);
+	        alpha=0x100-alpha;
+    	        if (grey + alpha > 255) grey = 256 - alpha;
+    	        if (cu + alpha > 255) cu = 256 - alpha;
+    	        if (cv + alpha > 255) cv = 256 - alpha;
+	        if(img->alpha) {if(grey>0x10) alpha=1; else alpha=0;}
+	        *iptr=grey;			// store 0,0 Y
+	        *iptr2=grey;			// store 0,1 Y
+	        *aptr=alpha;			// store alpha
+	        *aptr2=alpha;
+		iptr++;
+		aptr++;
+		iptr2++;
+		aptr2++;
+	        *iptr=grey;			// store 1,0 Y
+	        *iptr2=grey;			// store 1,1 Y
+	        *aptr=alpha;
+	        *aptr2=alpha;
+	    	if(alpha) {
+		    *uptr=cu;			// store U
+		    *vptr=cv;}			// store V
+		*auvptr=alpha;
+		uptr++;
+		vptr++;
+		auvptr++;
+		iptr++;
+		aptr++;		
+		iptr2++;
+		aptr2++;		
+		pin++;
+		}
+	    }
+
+#else
+        for(py=hs;py<img->rows*h1;py++) {
+    	    iptr=spu->image+(py-hs)*spu->stride;		// image ptr (Y planes)
+	    aptr=spu->aimage+(py-hs)*spu->stride;		// alpha ptr
+	    uptr=spu->imageu+((py-hs)/2)*spu->strideuv;		// u plane
+	    vptr=spu->imagev+((py-hs)/2)*spu->strideuv;		// v plane
+	    auvptr=spu->aimageuv+((py-hs)/2)*spu->strideuv;
+	    for(px=0;px<img->columns*12;px++) {
+	        grey=rgbtoy(VBI_R(*pin),VBI_G(*pin),VBI_B(*pin));	// RGB to Y
+	        cu=rgbtou(VBI_R(*pin),VBI_G(*pin),VBI_B(*pin));		// RGB to U
+	        cv=rgbtov(VBI_R(*pin),VBI_G(*pin),VBI_B(*pin));		// RGB to V
+	        alpha=VBI_A(*pin);
+	        alpha=0x100-alpha;
+    	        if (grey + alpha > 255) grey = 256 - alpha;
+    	        if (cu + alpha > 255) cu = 256 - alpha;
+    	        if (cv + alpha > 255) cv = 256 - alpha;
+	        if(img->alpha) {if(grey>0x10) alpha=1; else alpha=0;}
+	        *iptr=grey;			// store Y plane
+	        *aptr=alpha;
+	        if((px % 2)==0 && (py%2)==0) {	// pair line, pair pos
+	    	    if(alpha) {
+		        *uptr=cu;		// store U plane
+			*vptr=cv;}		// store V plane
+		    *auvptr=alpha;
+		    uptr++;
+		    vptr++;
+		    auvptr++;
+		    }
+		*auvptr=alpha;
+		uptr++;
+		vptr++;
+		auvptr++;
+		iptr++;
+		aptr++;		
+		pin++;
+		}
+	    }
+#endif
+	spu->dvdnav_menu=1;
+	break;
+    case DVDNAV_SPU_RGB:
+    case DVDNAV_SPU_BGR:
+    	for(py=hs;py<img->rows*h1;py++) {
+	    iptr=spu->image+(py-hs)*spu->stride;	// image ptr (R or B plane)
+	    aptr=spu->aimage+(py-hs)*spu->stride;
+	    uptr=spu->imageu+(py-hs)*spu->strideuv;	// G plane
+	    vptr=spu->imagev+(py-hs)*spu->strideuv;	// B or R plane
+	    for(px=0;px<img->columns*12;px++) {
+		grey=rgbtoy(VBI_R(*pin),VBI_G(*pin),VBI_B(*pin));	// RGB to Y
+		alpha=VBI_A(*pin);
+		alpha=0x100-alpha;
+    		if (grey + alpha > 255) grey = 256 - alpha;
+		if(img->alpha) {if(grey>0x10) alpha=1; else alpha=0;}
+		*iptr=VBI_R(*pin);			// store R plane
+		*uptr=VBI_G(*pin);			// store G plane
+		*vptr=VBI_B(*pin);			// store B plane
+		*aptr=alpha;
+		uptr++;
+		vptr++;
+		iptr++;
+		aptr++;		
+		pin++;
+		}
+	    }
+	spu->dvdnav_menu=1;
+	break;
+    default:
+	for(py=hs;py<img->rows*h1;py++) {
+	    iptr=spu->image+(py-hs)*spu->stride;	// image ptr
+	    aptr=spu->aimage+(py-hs)*spu->stride;	// alpha ptr
+	    for(px=0;px<img->columns*12;px++) {
+		grey=rgbtoy(VBI_R(*pin),VBI_G(*pin),VBI_B(*pin));	// RGB to Y
+		alpha=VBI_A(*pin);
+		switch (img->tformat) {
+		    case 0x01:	// BW
+			if(grey>0x10) grey=0xff; else grey=0x00;	// Black or White
+			alpha=1;
+			break;
+		    case 0x02:	// Gray
+		    case 0x03:	// Color (not supported)
+			alpha=0x100-alpha;
+    			if (grey + alpha > 255) grey = 256 - alpha;
+			break;
+		    }
+		if(img->alpha) {
+		if(grey>0x10) alpha=1; else alpha=0;
+		}
+	    *iptr=grey;		// store Y plane
+	    *aptr=alpha;	// store alpha
+	    iptr++;
+	    aptr++;		
+	    pin++;
+	    }
+	}
+    }
+spu->start_pts=0;
+spu->end_pts=UINT_MAX;
+}
+#endif
--- mplayer.c	 (revision 22772)
+++ mplayer.c	 (working copy)
@@ -85,7 +85,7 @@
 int player_idle_mode=0;
 int quiet=0;
 int enable_mouse_movements=0;
-
+int dvdnav_color_spu_flg=-1;
 #ifdef WIN32
 char * proc_priority=NULL;
 #endif
@@ -1036,7 +1036,10 @@
     vo_spudec=spudec_new_scaled(palette, mpctx->sh_video->disp_w, mpctx->sh_video->disp_h);
     spudec_set_font_factor(vo_spudec,font_factor);
   }
-
+#ifdef HAVE_TV_TELETEXT
+    if (vo_spudec==NULL && mpctx->demuxer->type==DEMUXER_TYPE_TV)
+	vo_spudec=spudec_new_scaled(NULL, mpctx->sh_video->disp_w, mpctx->sh_video->disp_h);
+#endif
   if (vo_spudec!=NULL)
     inited_flags|=INITED_SPUDEC;
 }
@@ -1619,6 +1622,7 @@
 	decoded_frame = decode_video(sh_video, start, in_size, 0, pts);
 	if (decoded_frame) {
 	    update_subtitles(sh_video, mpctx->d_sub, 0);
+	    update_teletext(sh_video, mpctx->demuxer, 0);
 	    update_osd_msg();
 	    current_module = "filter video";
 	    if (filter_video(sh_video, decoded_frame, sh_video->pts))
@@ -2033,6 +2037,7 @@
 	    ++total_frame_cnt;
 	}
 	update_subtitles(sh_video, mpctx->d_sub, 0);
+	update_teletext(sh_video, mpctx->demuxer, 0);
 	update_osd_msg();
 	current_module = "decode_video";
 	decoded_frame = decode_video(sh_video, start, in_size, drop_frame,
@@ -2246,6 +2251,7 @@
 	// be completely wrong (probably 0).
 	mpctx->sh_video->pts = mpctx->d_video->pts;
 	update_subtitles(mpctx->sh_video, mpctx->d_sub, 1);
+        update_teletext(mpctx->sh_video, mpctx->demuxer, 1);
     }
       
     if (mpctx->sh_audio) {
@@ -3116,7 +3122,11 @@
 
 //================== Read SUBTITLES (DVD & TEXT) ==========================
 if(vo_spudec==NULL && mpctx->sh_video &&
-     (mpctx->stream->type==STREAMTYPE_DVD || mpctx->stream->type == STREAMTYPE_DVDNAV || mpctx->d_sub->id >= 0)){
+     (mpctx->stream->type==STREAMTYPE_DVD || mpctx->stream->type == STREAMTYPE_DVDNAV || 
+#ifdef HAVE_TV_TELETEXT
+    mpctx->demuxer->type==DEMUXER_TYPE_TV ||
+#endif
+     mpctx->d_sub->id >= 0)){
   init_vo_spudec();
 }
 
@@ -3470,6 +3480,22 @@
    vo_osd_changed (OSDTYPE_DVDNAV);
  }
 #endif
+if (dvdnav_color_spu_flg==-1) {					// is unsettings vo color mode
+    dvdnav_color_spu_flg=0;
+    if (mpctx->video_out) {					// query vo color mode
+        if (!dvdnav_color_spu_flg && mpctx->video_out->control(VOCTRL_YUVSPU_SUPPORT,NULL)==VO_TRUE)
+	    dvdnav_color_spu_flg=DVDNAV_SPU_YUV;
+	if (!dvdnav_color_spu_flg && mpctx->video_out->control(VOCTRL_YUYSPU_SUPPORT,NULL)==VO_TRUE)
+	    dvdnav_color_spu_flg=DVDNAV_SPU_YUY;
+	if (!dvdnav_color_spu_flg && mpctx->video_out->control(VOCTRL_RGBSPU_SUPPORT,NULL)==VO_TRUE)
+	    dvdnav_color_spu_flg=DVDNAV_SPU_RGB;
+	if (!dvdnav_color_spu_flg && mpctx->video_out->control(VOCTRL_BGRSPU_SUPPORT,NULL)==VO_TRUE)
+	    dvdnav_color_spu_flg=DVDNAV_SPU_BGR;
+//	if (!dvdnav_color_spu_flg)
+//	    mp_msg(MSGT_CPLAYER,MSGL_INFO,MSGTR_MPDVDNAV_NoColorSpu);
+	}
+    spudec_dvdnav_mode(vo_spudec, 0, dvdnav_color_spu_flg);
+    }
  
 //============================ Handle PAUSE ===============================
 
--- command.c	 (revision 22772)
+++ command.c	 (working copy)
@@ -1288,8 +1288,131 @@
 
 #endif
 
+#ifdef HAVE_TV_TELETEXT
+/// VBI teletext on/off page (RW)
+static int mp_property_vbi_switch(m_option_t * prop, int action, void *arg,
+				  MPContext * mpctx)
+{
+    tvi_handle_t *tvh = mpctx->demuxer->priv;
+    if (mpctx->demuxer->type != DEMUXER_TYPE_TV || !tvh)
+	return M_PROPERTY_UNAVAILABLE;
+
+    switch (action) {
+    case M_PROPERTY_SET:
+    case M_PROPERTY_STEP_UP:
+    case M_PROPERTY_STEP_DOWN:
+    default:
+	return tv_teletext_switch(tvh, -1);
+    }
+}
+///@}
+
+static int mp_property_vbi_step_page(m_option_t * prop, int action, void *arg,
+				  MPContext * mpctx)
+{
+    tvi_handle_t *tvh = mpctx->demuxer->priv;
+    if (mpctx->demuxer->type != DEMUXER_TYPE_TV || !tvh)
+	return M_PROPERTY_UNAVAILABLE;
+
+    switch (action) {
+    case M_PROPERTY_SET:
+	if (!arg)
+	    return M_PROPERTY_ERROR;
+	M_PROPERTY_CLAMP(prop, *(int *) arg);
+	return tv_step_teletext_page(tvh, *(int *) arg);
+    case M_PROPERTY_STEP_UP:
+	return tv_step_teletext_page(tvh, 1);
+    case M_PROPERTY_STEP_DOWN:
+	return tv_step_teletext_page(tvh, -1);
+    default:
+	return M_PROPERTY_UNAVAILABLE;
+    }
+}
+///@}
+
+static int mp_property_vbi_set_page(m_option_t * prop, int action, void *arg,
+				  MPContext * mpctx)
+{
+    tvi_handle_t *tvh = mpctx->demuxer->priv;
+    if (mpctx->demuxer->type != DEMUXER_TYPE_TV || !tvh)
+	return M_PROPERTY_UNAVAILABLE;
+
+    switch (action) {
+    case M_PROPERTY_SET:
+	if (!arg)
+	    return M_PROPERTY_ERROR;
+	M_PROPERTY_CLAMP(prop, *(int *) arg);
+	return tv_set_teletext_page(tvh, *(int *) arg);
+    case M_PROPERTY_STEP_UP:
+	return tv_step_teletext_page(tvh, 1);
+    case M_PROPERTY_STEP_DOWN:
+	return tv_step_teletext_page(tvh, -1);
+    default:
+	return M_PROPERTY_UNAVAILABLE;
+    }
+}
+///@}
+
+/// VBI teletext flip page (RW)
+static int mp_property_vbi_flip_page(m_option_t * prop, int action, void *arg,
+				  MPContext * mpctx)
+{
+
+//    if (!vo_osd_teletex_text)
+//	return M_PROPERTY_UNAVAILABLE;
+    switch (action) {
+    case M_PROPERTY_SET:
+	if (!arg)
+	    return M_PROPERTY_ERROR;
+	M_PROPERTY_CLAMP(prop, *(int *) arg);
+	if (vo_osd_teletext_flip == !!*(int *) arg)
+	    return M_PROPERTY_OK;
+    case M_PROPERTY_STEP_UP:
+    case M_PROPERTY_STEP_DOWN:
+	vo_osd_teletext_flip=!vo_osd_teletext_flip;
+	return M_PROPERTY_OK;
+    default:
+	return m_property_flag(prop, action, arg, &vo_osd_teletext_flip);
+    }
+}
 ///@}
 
+static int mp_property_vbi_alpha_page(m_option_t * prop, int action, void *arg,
+				  MPContext * mpctx)
+{
+    tvi_handle_t *tvh = mpctx->demuxer->priv;
+    if (mpctx->demuxer->type != DEMUXER_TYPE_TV || !tvh || !vo_spudec)
+	return M_PROPERTY_UNAVAILABLE;
+
+    switch (action) {
+    case M_PROPERTY_SET:
+	return tv_teletext_alpha_page(tvh, *(int *) arg);
+    case M_PROPERTY_STEP_UP:
+    case M_PROPERTY_STEP_DOWN:
+    default:
+	return tv_teletext_alpha_page(tvh, -1);
+    }
+}
+
+static int mp_property_vbi_half_page(m_option_t * prop, int action, void *arg,
+				  MPContext * mpctx)
+{
+    tvi_handle_t *tvh = mpctx->demuxer->priv;
+    if (mpctx->demuxer->type != DEMUXER_TYPE_TV || !tvh || !vo_spudec)
+	return M_PROPERTY_UNAVAILABLE;
+
+    switch (action) {
+    case M_PROPERTY_SET:
+	return tv_teletext_half_page(tvh, *(int *) arg);
+    case M_PROPERTY_STEP_UP:
+    case M_PROPERTY_STEP_DOWN:
+    default:
+	return tv_teletext_half_page(tvh, -1);
+    }
+}
+///@}
+#endif
+
 /// All properties available in MPlayer.
 /** \ingroup Properties
  */
@@ -1403,6 +1526,21 @@
      M_OPT_RANGE, -100, 100, (void *) TV_COLOR_HUE },
 #endif
 
+#ifdef HAVE_TV_TELETEXT
+    { "vbi_switch", mp_property_vbi_switch, CONF_TYPE_FLAG,
+     M_OPT_RANGE, 0, 1, NULL },
+    { "vbi_switch_alpha", mp_property_vbi_alpha_page, CONF_TYPE_FLAG,
+     M_OPT_RANGE, 0, 1, NULL },
+    { "vbi_half_page", mp_property_vbi_half_page, CONF_TYPE_FLAG,
+     M_OPT_RANGE, 0, 2, NULL },
+    { "vbi_flip_page", mp_property_vbi_flip_page, CONF_TYPE_FLAG,
+     M_OPT_RANGE, 0, 1, NULL },
+    { "vbi_step_page", mp_property_vbi_step_page, CONF_TYPE_INT,
+     M_OPT_RANGE, -999, 999, NULL },
+    { "vbi_set_page", mp_property_vbi_set_page, CONF_TYPE_INT,
+     M_OPT_RANGE, 100, 999, NULL },
+#endif
+
     { NULL, NULL, NULL, 0, 0, 0, NULL }
 };
 
@@ -1500,6 +1638,14 @@
     { "tv_saturation", MP_CMD_TV_SET_SATURATION, 0, OSD_SATURATION, -1, MSGTR_Saturation },
     { "tv_contrast", MP_CMD_TV_SET_CONTRAST, 0, OSD_CONTRAST, -1, MSGTR_Contrast },
 #endif
+#ifdef HAVE_TV_TELETEXT
+    { "vbi_switch", MP_CMD_VBI_SWITCH, 1, 0, -1, NULL },
+    { "vbi_switch_alpha", MP_CMD_VBI_ALPHA_PAGE, 1, 0, -1, NULL },
+    { "vbi_half_page", MP_CMD_VBI_HALF_PAGE, 1, 0, -1, NULL },
+    { "vbi_set_page", MP_CMD_VBI_SET_PAGE, 0, 0, -1, NULL },
+    { "vbi_step_page", MP_CMD_VBI_STEP_PAGE, 1, 0, -1, NULL },
+    { "vbi_flip_page", MP_CMD_VBI_FLIP_PAGE, 1, 0, -1, NULL },
+#endif
     { NULL, 0, 0, 0, -1, NULL }
 };
 
--- stream/Makefile	 (revision 22772)
+++ stream/Makefile	 (working copy)
@@ -51,6 +51,7 @@
 SRCS_COMMON-$(TV_BSDBT848)       += tvi_bsdbt848.c
 SRCS_COMMON-$(TV_V4L1)           += tvi_v4l.c  audio_in.c
 SRCS_COMMON-$(TV_V4L2)           += tvi_v4l2.c audio_in.c
+SRCS_COMMON-$(TV_TELETEXT)	 += tvi_vbi.c
 SRCS_COMMON-$(VCD)               += stream_vcd.c
 SRCS_COMMON-$(VSTREAM)           += stream_vstream.c
 
--- stream/tv.h	 (revision 22772)
+++ stream/tv.h	 (working copy)
@@ -45,6 +45,11 @@
 #endif
 extern char* tv_param_adevice;
 #endif
+#ifdef HAVE_TV_TELETEXT
+extern char* tv_param_tdevice;		// teletext vbi device
+extern char* tv_param_tformat;		// format: text,bw,gray,color
+extern char* tv_param_tpage;		// first page number
+#endif
 extern int tv_param_brightness;
 extern int tv_param_contrast;
 extern int tv_param_hue;
@@ -52,7 +57,7 @@
 
 typedef struct tvi_info_s
 {
-    struct tvi_handle_s * (*tvi_init)(char *device,char *adevice);
+    struct tvi_handle_s * (*tvi_init)(char *device,char *adevice, char *tdevice, char *tformat, char *tpage);
     const char *name;
     const char *short_name;
     const char *author;
@@ -95,6 +100,17 @@
     struct tv_channels_s *prev;
 } tv_channels_t;
 
+#ifdef HAVE_TV_TELETEXT
+typedef struct tv_teletext_img_s {
+    void*	canvas;
+    int		tformat;
+    int		columns;
+    int		rows;
+    int		half;
+    int		alpha;
+} tv_teletext_img_t;
+#endif
+
 extern tv_channels_t *tv_channel_list;
 extern tv_channels_t *tv_channel_current, *tv_channel_last;
 extern char *tv_channel_last_real;
@@ -158,6 +174,16 @@
 #define TVI_CONTROL_SPC_SET_INPUT	0x402	/* set input channel (tv,s-video,composite..) */
 #define TVI_CONTROL_SPC_GET_NORMID	0x403	/* get normid from norm name */
 
+/* TELETEX controls */
+#define TVI_CONTROL_VBI_SWITCH		0x501	/* on/off garb teletext */
+#define TVI_CONTROL_VBI_SET_PAGE	0x502	/* set garb teletext page number */
+#define TVI_CONTROL_VBI_STEP_PAGE	0x503	/* set garb teletext page number */
+#define TVI_CONTROL_VBI_GET_PAGE	0x504	/* get garbbed teletext page */
+#define TVI_CONTROL_VBI_ALPHA_PAGE	0x505	/* switch alpha */
+#define TVI_CONTROL_VBI_HALF_PAGE	0x506	/* switch half page */
+#define TVI_CONTROL_VBI_GET_TXTPAGE	0x507	/* get garbbed text teletext page */
+#define TVI_CONTROL_VBI_GET_IMGPAGE	0x508	/* get garbbed image teletext page */
+
 extern tvi_handle_t *tv_begin(void);
 extern int tv_init(tvi_handle_t *tvh);
 extern int tv_uninit(tvi_handle_t *tvh);
@@ -188,6 +214,25 @@
 
 int tv_set_norm(tvi_handle_t *tvh, char* norm);
 
+#ifdef HAVE_TV_TELETEXT
+// teletext on/off
+int tv_teletext_switch(tvi_handle_t *tvh, int flag);
+// oacity page mode on/off
+int tv_teletext_alpha_page(tvi_handle_t *tvh, int flag);
+// half mode: 0: off, 1:top half page, 2: bottom half page
+int tv_teletext_half_page(tvi_handle_t *tvh, int flag);
+// go to pageno page
+int tv_set_teletext_page(tvi_handle_t *tvh, int pageno);
+// add/sub page number
+int tv_step_teletext_page(tvi_handle_t *tvh, int direction);
+// get current vbi_page
+void* tv_get_teletext_page(tvi_handle_t *tvh);
+// get current page text
+char* tv_get_teletext_txtpage(tvi_handle_t *tvh);
+// get current page image (RGB32_LB format)
+tv_teletext_img_t* tv_get_teletext_imgpage(tvi_handle_t *tvh);
+#endif
+
 #define TV_NORM_PAL		1
 #define TV_NORM_NTSC		2
 #define TV_NORM_SECAM		3
--- stream/tv.c	 (revision 22772)
+++ stream/tv.c	 (working copy)
@@ -36,6 +36,10 @@
 
 #include "frequencies.h"
 
+#ifdef HAVE_TV_TELETEXT
+#include "tvi_vbi.h"
+#endif
+
 /* some default values */
 int tv_param_audiorate = 44100;
 int tv_param_noaudio = 0;
@@ -73,6 +77,9 @@
 #endif
 #endif
 char* tv_param_adevice = NULL;
+char* tv_param_tdevice = NULL;
+char* tv_param_tformat = NULL;
+char* tv_param_tpage = NULL;
 int tv_param_brightness = 0;
 int tv_param_contrast = 0;
 int tv_param_hue = 0;
@@ -685,10 +692,10 @@
 	}
 	return NULL;
     }
-
     for(i=0;tvi_driver_list[i];i++){
         if (!strcmp(tvi_driver_list[i]->short_name, tv_param_driver)){
-            h=tvi_driver_list[i]->tvi_init(tv_param_device,tv_param_adevice);
+            h=tvi_driver_list[i]->tvi_init(tv_param_device,tv_param_adevice,
+		tv_param_tdevice,tv_param_tformat,tv_param_tpage);
             if(!h) return NULL;
 
             mp_msg(MSGT_TV, MSGL_INFO, "Selected driver: %s\n", tvi_driver_list[i]->short_name);
@@ -960,6 +967,67 @@
     return(1);
 }
 
+#ifdef HAVE_TV_TELETEXT
+int tv_teletext_switch(tvi_handle_t *tvh, int flag)
+{
+if (tvh->functions->control(tvh->priv, TVI_CONTROL_VBI_SWITCH, &flag)
+    != TVI_CONTROL_TRUE) return 0;
+return 1;
+}
+
+int tv_teletext_alpha_page(tvi_handle_t *tvh, int flag)
+{
+if (tvh->functions->control(tvh->priv, TVI_CONTROL_VBI_ALPHA_PAGE, &flag)
+    != TVI_CONTROL_TRUE) return 0;
+return 1;
+}
+
+int tv_teletext_half_page(tvi_handle_t *tvh, int flag)
+{
+if (tvh->functions->control(tvh->priv, TVI_CONTROL_VBI_HALF_PAGE, &flag)
+    != TVI_CONTROL_TRUE) return 0;
+return 1;
+}
+
+int tv_set_teletext_page(tvi_handle_t *tvh, int pageno)
+{
+if (tvh->functions->control(tvh->priv, TVI_CONTROL_VBI_SET_PAGE, &pageno)
+    != TVI_CONTROL_TRUE) return 0;
+return 1;
+}
+
+int tv_step_teletext_page(tvi_handle_t *tvh, int direction)
+{
+if (tvh->functions->control(tvh->priv, TVI_CONTROL_VBI_STEP_PAGE, &direction)
+    != TVI_CONTROL_TRUE) return 0;
+return 1;
+}
+
+void* tv_get_teletext_page(tvi_handle_t *tvh)
+{
+void* page = NULL;
+if (tvh->functions->control(tvh->priv, TVI_CONTROL_VBI_GET_PAGE, &page)
+    != TVI_CONTROL_TRUE) return NULL;
+return page;
+}
+
+char* tv_get_teletext_txtpage(tvi_handle_t *tvh)
+{
+char* page = NULL;
+if (tvh->functions->control(tvh->priv, TVI_CONTROL_VBI_GET_TXTPAGE, &page)
+    != TVI_CONTROL_TRUE) return NULL;
+return page;
+}
+
+tv_teletext_img_t* tv_get_teletext_imgpage(tvi_handle_t *tvh)
+{
+tv_teletext_img_t* tv_teletext_img = NULL;
+if (tvh->functions->control(tvh->priv, TVI_CONTROL_VBI_GET_IMGPAGE, &tv_teletext_img)
+    != TVI_CONTROL_TRUE) return NULL;
+return tv_teletext_img;
+}
+#endif
+
 demuxer_desc_t demuxer_desc_tv = {
   "Tv card demuxer",
   "tv",
--- stream/tvi_dummy.c	 (revision 22772)
+++ stream/tvi_dummy.c	 (working copy)
@@ -8,7 +8,7 @@
 #include "libmpcodecs/img_format.h"
 #include "tv.h"
 
-static tvi_handle_t *tvi_init_dummy(char *device,char *adevice);
+static tvi_handle_t *tvi_init_dummy(char *device,char *adevice, char *tdevice, char *tformat, char *tpage);
 /* information about this file */
 tvi_info_t tvi_info_dummy = {
 	tvi_init_dummy,
@@ -27,7 +27,7 @@
 #include "tvi_def.h"
 
 /* handler creator - entry point ! */
-static tvi_handle_t *tvi_init_dummy(char *device,char *adevice)
+static tvi_handle_t *tvi_init_dummy(char *device,char *adevice, char *tdevice, char *tformat, char *tpage)
 {
     return(new_handle());
 }
--- stream/tvi_bsdbt848.c	 (revision 22772)
+++ stream/tvi_bsdbt848.c	 (working copy)
@@ -66,7 +66,7 @@
 #include "libmpcodecs/img_format.h"
 #include "tv.h"
 
-static tvi_handle_t *tvi_init_bsdbt848(char *device, char *adevice);
+static tvi_handle_t *tvi_init_bsdbt848(char *device, char *adevice, char *tdevice, char *tformat, char *tpage);
 /* information about this file */
 tvi_info_t tvi_info_bsdbt848 = {
     tvi_init_bsdbt848,
@@ -171,7 +171,7 @@
 }
 
 /* handler creator - entry point ! */
-static tvi_handle_t *tvi_init_bsdbt848(char *device,char* adevice)
+static tvi_handle_t *tvi_init_bsdbt848(char *device,char* adevice, char *tdevice, char *tformat, char *tpage)
 {
     return(new_handle());
 }
--- stream/tvi_v4l.c	 (revision 22772)
+++ stream/tvi_v4l.c	 (working copy)
@@ -43,12 +43,15 @@
 #include "libmpcodecs/img_format.h"
 #include "libvo/fastmemcpy.h"
 #include "libvo/videodev_mjpeg.h"
+#ifdef HAVE_TV_TELETEXT
+#include "tvi_vbi.h"
+#endif
 
 #include "tv.h"
 
 #include "audio_in.h"
 
-static tvi_handle_t *tvi_init_v4l(char *device, char *adevice);
+static tvi_handle_t *tvi_init_v4l(char *device, char *adevice, char *tdevice, char *tformat, char *tpage);
 
 tvi_info_t tvi_info_v4l = {
 	tvi_init_v4l,
@@ -142,7 +145,9 @@
     long			audio_recv_blocks_total;
     long			audio_sent_blocks_total;
     long                        mjpeg_bufsize;
-    
+#ifdef HAVE_TV_TELETEXT
+    priv_vbi_t			*priv_vbi;
+#endif
 } priv_t;
 
 #include "tvi_def.h"
@@ -269,7 +274,7 @@
 	   priv->audio_buffer_size, priv->audio_in.blocksize, priv->aud_skew_cnt);
 }
 
-static tvi_handle_t *tvi_init_v4l(char *device, char *adevice)
+static tvi_handle_t *tvi_init_v4l(char *device, char *adevice, char *tdevice, char *tformat, char *tpage)
 {
     tvi_handle_t *h;
     priv_t *priv;
@@ -299,6 +304,10 @@
 	return(NULL);
     }
 
+#ifdef HAVE_TV_TELETEXT
+    if(tdevice) priv->priv_vbi=vbi_init(tdevice,tformat,tpage);
+#endif
+
     return(h);
 }
 
@@ -685,6 +694,12 @@
     unsigned long num;
     priv->shutdown = 1;
 
+#ifdef HAVE_TV_TELETEXT
+    if(priv->priv_vbi) {
+	vbi_uninit(priv->priv_vbi);
+	priv->priv_vbi=NULL;
+	}
+#endif
     mp_msg(MSGT_TV, MSGL_V, "Waiting for threads to finish... ");
     if (!tv_param_noaudio) {
 	pthread_join(priv->audio_grabber_thread, NULL);
@@ -1155,6 +1170,9 @@
 		ioctl(priv->video_fd, VIDIOCSAUDIO, &priv->audio[priv->audio_id]);
 	    }
 
+#ifdef HAVE_TV_TELETEXT
+	    if(priv->priv_vbi) vbi_reset(priv->priv_vbi);
+#endif
 	    return(TVI_CONTROL_TRUE);
 	}
 	case TVI_CONTROL_TUN_GET_TUNER:
@@ -1176,6 +1194,9 @@
 		mp_msg(MSGT_TV, MSGL_ERR, "ioctl set tuner failed: %s\n", strerror(errno));
 		return(TVI_CONTROL_FALSE);
 	    }
+#ifdef HAVE_TV_TELETEXT
+	    if(priv->priv_vbi) vbi_reset(priv->priv_vbi);
+#endif
 	    return(TVI_CONTROL_TRUE);
 	}
 	case TVI_CONTROL_TUN_SET_NORM:
@@ -1377,6 +1398,49 @@
 	case TVI_CONTROL_IMMEDIATE:
 	    priv->immediate_mode = 1;
 	    return(TVI_CONTROL_TRUE);
+#ifdef HAVE_TV_TELETEXT
+        case TVI_CONTROL_VBI_SWITCH:
+	    if (!priv->priv_vbi) return TVI_CONTROL_FALSE;
+	    vbi_switch(priv->priv_vbi, *(int *)arg);
+	    return TVI_CONTROL_TRUE;
+        case TVI_CONTROL_VBI_ALPHA_PAGE:
+	    if (!priv->priv_vbi) return TVI_CONTROL_FALSE;
+	    vbi_switch_alpha(priv->priv_vbi, *(int *)arg);
+	    return TVI_CONTROL_TRUE;
+        case TVI_CONTROL_VBI_HALF_PAGE:
+	    if (!priv->priv_vbi) return TVI_CONTROL_FALSE;
+	    vbi_switch_half(priv->priv_vbi, *(int *)arg);
+	    return TVI_CONTROL_TRUE;
+        case TVI_CONTROL_VBI_SET_PAGE:
+	    if (!priv->priv_vbi) return TVI_CONTROL_FALSE;
+	    vbi_setpage(priv->priv_vbi, *(int *)arg, 0);
+	    return TVI_CONTROL_TRUE;
+        case TVI_CONTROL_VBI_STEP_PAGE:
+	    if (!priv->priv_vbi) return TVI_CONTROL_FALSE;
+	    vbi_steppage(priv->priv_vbi, *(int *)arg);
+	    return TVI_CONTROL_TRUE;
+	case TVI_CONTROL_VBI_GET_PAGE:
+	    if (!priv->priv_vbi) return TVI_CONTROL_FALSE;
+	    vbi_page *page = NULL;
+	    if (NULL==(page=vbi_getpage(priv->priv_vbi)))
+		return TVI_CONTROL_FALSE;
+	    *(void **)arg = (void*)page;
+	    return TVI_CONTROL_TRUE;
+	case TVI_CONTROL_VBI_GET_TXTPAGE:
+	    if (!priv->priv_vbi) return TVI_CONTROL_FALSE;
+	    char *txtpage = NULL;
+	    if (NULL==(txtpage=vbi_getpagetext(priv->priv_vbi)))
+		return TVI_CONTROL_FALSE;
+	    *(void **)arg = (void*)txtpage;
+	    return TVI_CONTROL_TRUE;
+	case TVI_CONTROL_VBI_GET_IMGPAGE:
+	    if (!priv->priv_vbi) return TVI_CONTROL_FALSE;
+	    tv_teletext_img_t *img = NULL;
+	    if (NULL==(img=vbi_getpageimg(priv->priv_vbi)))
+		return TVI_CONTROL_FALSE;
+	    *(void **)arg = (void*)img;
+	    return TVI_CONTROL_TRUE;
+#endif
 	}
     }
 
--- stream/tvi_v4l2.c	 (revision 22772)
+++ stream/tvi_v4l2.c	 (working copy)
@@ -45,9 +45,12 @@
 #include "libaf/af_format.h"
 #include "tv.h"
 #include "audio_in.h"
+#ifdef HAVE_TV_TELETEXT
+#include "tvi_vbi.h"
+#endif
 
 #define info tvi_info_v4l2
-static tvi_handle_t *tvi_init_v4l2(char *video_dev, char *audio_dev);
+static tvi_handle_t *tvi_init_v4l2(char *video_dev, char *audio_dev, char *tdevice, char *tformat, char *tpage);
 /* information about this file */
 tvi_info_t tvi_info_v4l2 = {
     tvi_init_v4l2,
@@ -131,6 +134,12 @@
     volatile long               audio_null_blocks_inserted;
     volatile long long          dropped_frames_timeshift;
     long long                   dropped_frames_compensated;
+#ifdef HAVE_TV_TELETEXT
+    char			*tdevice;
+    char			*tformat;
+    char			*tpage;
+    priv_vbi_t			*priv_vbi;
+#endif
 } priv_t;
 
 #include "tvi_def.h"
@@ -702,6 +711,9 @@
 	usleep(100000); // wait to suppress noise during switching
 	set_mute(priv, 0);
 #endif
+#ifdef HAVE_TV_TELETEXT
+	if(priv->priv_vbi) vbi_reset(priv->priv_vbi);
+#endif
 	return TVI_CONTROL_TRUE;
     case TVI_CONTROL_TUN_GET_TUNER:
 	mp_msg(MSGT_TV, MSGL_V, "%s: get tuner\n",info.short_name);
@@ -716,6 +728,9 @@
 	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));
+#ifdef HAVE_TV_TELETEXT
+	    if(priv->priv_vbi) vbi_reset(priv->priv_vbi);
+#endif
 	    return TVI_CONTROL_FALSE;
 	}
 	return TVI_CONTROL_TRUE;
@@ -808,6 +823,49 @@
 	if (audio_in_set_samplerate(&priv->audio_in, *(int*)arg) < 0) return TVI_CONTROL_FALSE;
 //	setup_audio_buffer_sizes(priv);
 	return TVI_CONTROL_TRUE;
+#ifdef HAVE_TV_TELETEXT
+    case TVI_CONTROL_VBI_SWITCH:
+	if (!priv->priv_vbi) return TVI_CONTROL_FALSE;
+	vbi_switch(priv->priv_vbi, *(int *)arg);
+	return TVI_CONTROL_TRUE;
+    case TVI_CONTROL_VBI_ALPHA_PAGE:
+	if (!priv->priv_vbi) return TVI_CONTROL_FALSE;
+	vbi_switch_alpha(priv->priv_vbi, *(int *)arg);
+	return TVI_CONTROL_TRUE;
+    case TVI_CONTROL_VBI_HALF_PAGE:
+	if (!priv->priv_vbi) return TVI_CONTROL_FALSE;
+	vbi_switch_half(priv->priv_vbi, *(int *)arg);
+	return TVI_CONTROL_TRUE;
+    case TVI_CONTROL_VBI_SET_PAGE:
+	if (!priv->priv_vbi) return TVI_CONTROL_FALSE;
+	vbi_setpage(priv->priv_vbi, *(int *)arg, 0);
+	return TVI_CONTROL_TRUE;
+    case TVI_CONTROL_VBI_STEP_PAGE:
+	if (!priv->priv_vbi) return TVI_CONTROL_FALSE;
+	vbi_steppage(priv->priv_vbi, *(int *)arg);
+	return TVI_CONTROL_TRUE;
+    case TVI_CONTROL_VBI_GET_PAGE:
+	if (!priv->priv_vbi) return TVI_CONTROL_FALSE;
+	vbi_page *page = NULL;
+	if (NULL==(page=vbi_getpage(priv->priv_vbi)))
+	    return TVI_CONTROL_FALSE;
+	*(void **)arg = (void*)page;
+	return TVI_CONTROL_TRUE;
+    case TVI_CONTROL_VBI_GET_TXTPAGE:
+	if (!priv->priv_vbi) return TVI_CONTROL_FALSE;
+	char *txtpage = NULL;
+	if (NULL==(txtpage=vbi_getpagetext(priv->priv_vbi)))
+	    return TVI_CONTROL_FALSE;
+	*(void **)arg = (void*)txtpage;
+	return TVI_CONTROL_TRUE;
+    case TVI_CONTROL_VBI_GET_IMGPAGE:
+	if (!priv->priv_vbi) return TVI_CONTROL_FALSE;
+	tv_teletext_img_t *img = NULL;
+	if (NULL==(img=vbi_getpageimg(priv->priv_vbi)))
+	    return TVI_CONTROL_FALSE;
+	*(void **)arg = (void*)img;
+	return TVI_CONTROL_TRUE;
+#endif
     }
     mp_msg(MSGT_TV, MSGL_V, "%s: unknown control: %d\n", info.short_name, cmd);
     return(TVI_CONTROL_UNKNOWN);
@@ -817,7 +875,7 @@
 #define PRIV ((priv_t *) (tvi_handle->priv))
 
 /* handler creator - entry point ! */
-static tvi_handle_t *tvi_init_v4l2(char *video_dev, char *audio_dev)
+static tvi_handle_t *tvi_init_v4l2(char *video_dev, char *audio_dev, char *tdevice, char *tformat, char *tpage)
 {
     tvi_handle_t *tvi_handle;
 
@@ -834,6 +892,12 @@
 	return NULL;
     }
 
+#ifdef HAVE_TV_TELETEXT
+    PRIV->tdevice=tdevice;
+    PRIV->tformat=tformat;
+    PRIV->tpage=tpage;
+#endif
+
     if (audio_dev) {
 	PRIV->audio_dev = strdup(audio_dev);
 	if (!PRIV->audio_dev) {
@@ -853,6 +917,15 @@
 {
     int i, frames, dropped = 0;
 
+#ifdef HAVE_TV_TELETEXT
+    if(priv->priv_vbi) {
+	priv->tdevice=NULL;
+	priv->tformat=NULL;
+	priv->tpage=NULL;
+	vbi_uninit(priv->priv_vbi);
+	priv->priv_vbi=NULL;
+	}
+#endif
     priv->shutdown = 1;
     if(priv->video_grabber_thread)
 	pthread_join(priv->video_grabber_thread, NULL);
@@ -1116,7 +1189,10 @@
 	    set_control(priv, &control, 0);
 	}
     }
-
+#ifdef HAVE_TV_TELETEXT
+    if(priv->tdevice && priv->priv_vbi==NULL) 
+	priv->priv_vbi=vbi_init(priv->tdevice,priv->tformat,priv->tpage);
+#endif
     return 1;
 }
 
--- stream/tvi_vbi.h	 (none)
+++ stream/tvi_vbi.h	 (working copy)
@@ -0,0 +1,73 @@
+
+#include "libzvbi.h"
+#include "libmpcodecs/img_format.h"
+#include "libmpcodecs/mp_image.h"
+#include "tv.h"
+
+#define VBI_MAX_SUBPAGES 64				// max sub pages number
+#define VBI_TXT_PAGE_SIZE	42*25*2			// max text page size
+#define VBI_MAX_LINE_SIZE	42			// max line size in text page
+
+#define VBI_TFORMAT_TEXT	0			// text mode
+#define VBI_TFORMAT_BW		1			// back&white mode
+#define VBI_TFORMAT_GRAY	2			// grayscale mode
+#define VBI_TFORMAT_COLOR	3			// color mode (require color_spu patch!)
+
+#define VBI_NO_TELETEXT	"No teletext"
+
+typedef struct {
+    int			on;				// teletext on/off
+
+    char*		device;				// capture device
+    unsigned int	services;			// services
+    vbi_capture*	capture;			// vbi_capture
+    int			capture_fd;			// capture fd (now not used)
+    vbi_decoder*	decoder;			// vbi_decoder
+    char*		errstr;				// error string
+    pthread_t		grabber_thread;			// grab thread
+    pthread_mutex_t	buffer_mutex;
+    int			eof;				// end grab
+    int			pgno;				// seek page number
+    int			subno;				// seek subpage
+    int			curr_pgno;			// current page number
+    int			curr_subno;			// current subpage
+    int			act_subno;			// stored page number
+    int			act_pgno;			// stored subpage
+
+    vbi_page 		pages[VBI_MAX_SUBPAGES];	// vbi_pages
+    int			valid_pages[VBI_MAX_SUBPAGES];	// valid page flags
+    char*		txtpages;			// decoded vbi_page to text
+    char		header[VBI_MAX_LINE_SIZE];	// text header
+
+    int			tformat;			// 0:text, 1:bw, 2:gray, 3:color
+    vbi_pixfmt 		fmt;				// image format (only VBI_PIXFMT_RGBA32_LE supported)
+    void*		canvas;				// stored image data
+    int			csize;				// stored image size
+    int			canvas_size;			// image buffer size
+    int			reveal;				// reveal (now not used)
+    int			flash_on;			// flash_on (now not used)
+    int			alpha;				// opacity mode
+    int			half;				// 0:half mode off, 1:top half page, 2:bottom half page
+    int			redraw;				// is redraw last image
+    int			columns;			// page size: coloumns
+    int			rows;				// page size: rows
+    int			spudec_proc;			// render image request
+
+    char*		network_name;			// network name
+    char*		network_id;			// network id
+    } priv_vbi_t;
+
+priv_vbi_t* vbi_init(char *tdevice, char *tformat, char *tpage);
+void vbi_uninit(priv_vbi_t* priv_vbi);
+
+void vbi_switch(priv_vbi_t *priv_vbi, int flag);
+void vbi_reset(priv_vbi_t *priv_vbi);
+
+void vbi_switch_alpha(priv_vbi_t *priv_vbi, int flag);
+void vbi_switch_half(priv_vbi_t *priv_vbi, int flag);
+void vbi_setpage(priv_vbi_t *priv_vbi, int pgno, int subno);
+void vbi_steppage(priv_vbi_t *priv_vbi, int direct);
+vbi_page* vbi_getpage(priv_vbi_t *priv_vbi);
+char* vbi_getpagetext(priv_vbi_t *priv_vbi);
+tv_teletext_img_t* vbi_getpageimg(priv_vbi_t *priv_vbi);
+
--- stream/tvi_vbi.c	 (none)
+++ stream/tvi_vbi.c	 (working copy)
@@ -0,0 +1,741 @@
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "tvi_vbi.h"
+#include "mp_msg.h"
+#include "libmpcodecs/img_format.h"
+
+#ifdef USE_ICONV
+#include <iconv.h>
+#endif
+
+#define VBI_TEXT_CHARSET	"UTF-8"
+
+static void event_handler(vbi_event *ev, void *data);
+static void *grabber(void *data);
+
+static int inited = 0;
+
+/*
+// Open vbi
+*/
+priv_vbi_t *open_vbi(char *device, unsigned int services, int tformat, int startpage)
+{
+priv_vbi_t *priv_vbi;
+if(inited) return NULL;
+inited=1;
+if(NULL==(priv_vbi=malloc(sizeof(priv_vbi_t)))) return NULL;
+memset(priv_vbi,0,sizeof(priv_vbi_t));
+priv_vbi->device=strdup(device);
+priv_vbi->services=services;			// capture services
+if(priv_vbi->capture==NULL) {
+    priv_vbi->services=services;		// probe v4l2
+    priv_vbi->capture = vbi_capture_v4l2_new (priv_vbi->device,	/* device */
+	20,	/* buffer numbers */
+	&(priv_vbi->services),	/* services */
+	0,	/* strict */
+	&(priv_vbi->errstr),	/* error string */
+	0);	/* trace */
+    }
+services=priv_vbi->services;
+if(priv_vbi->capture==NULL) {
+    priv_vbi->services=services;		// probe v4l
+    priv_vbi->capture = vbi_capture_v4l_new (priv_vbi->device,
+	20,
+	&(priv_vbi->services),
+	0,
+	&(priv_vbi->errstr),
+	0);
+    }
+
+if(priv_vbi->capture==NULL) {
+    free(priv_vbi->device);
+    free(priv_vbi);
+    mp_msg(MSGT_STREAM,MSGL_INFO," No teletext\n");
+    return NULL;
+    }
+if(NULL!=(priv_vbi->txtpages=malloc(VBI_MAX_SUBPAGES*VBI_TXT_PAGE_SIZE)))	// alloc vbi_pages
+    memset(priv_vbi->txtpages,0,VBI_MAX_SUBPAGES*VBI_TXT_PAGE_SIZE);
+priv_vbi->decoder = vbi_decoder_new();
+priv_vbi->pgno=startpage;		// first page number
+priv_vbi->subno=0;
+priv_vbi->fmt=VBI_PIXFMT_RGBA32_LE;
+priv_vbi->tformat=tformat;
+snprintf(priv_vbi->header,sizeof(priv_vbi->header),"%s",VBI_NO_TELETEXT);
+vbi_event_handler_add(priv_vbi->decoder,~0,event_handler,(void*)priv_vbi);	// add event handler
+pthread_create(&priv_vbi->grabber_thread, NULL, grabber, priv_vbi);		// add grab function
+pthread_mutex_init(&priv_vbi->buffer_mutex, NULL);
+
+priv_vbi->act_subno=0;
+priv_vbi->valid_pages[priv_vbi->act_subno];
+mp_msg(MSGT_STREAM,MSGL_INFO," Teletext device: %s\n",priv_vbi->device);
+return priv_vbi;
+}
+
+
+/*
+// Close vbi capture, decodeand and free priv_vbi
+*/
+void close_vbi(priv_vbi_t *priv_vbi)
+{
+if(priv_vbi==NULL) return;
+priv_vbi->eof=1;
+if (priv_vbi->capture)
+    vbi_capture_delete(priv_vbi->capture);
+priv_vbi->capture=NULL;
+vbi_event_handler_remove(priv_vbi->decoder, event_handler);
+if (priv_vbi->decoder)
+    vbi_decoder_delete(priv_vbi->decoder);
+priv_vbi->decoder=NULL;
+if(priv_vbi->grabber_thread)
+    pthread_join(priv_vbi->grabber_thread, NULL);
+pthread_mutex_destroy(&priv_vbi->buffer_mutex);
+if(priv_vbi->device) 
+    free(priv_vbi->device);
+priv_vbi->device=NULL;
+if (priv_vbi->errstr)
+    free(priv_vbi->errstr);
+priv_vbi->errstr=NULL;
+if (priv_vbi->canvas)
+    free(priv_vbi->canvas);
+priv_vbi->canvas=NULL;
+if (priv_vbi->txtpages)
+    free(priv_vbi->txtpages);
+priv_vbi->txtpages=NULL;
+free(priv_vbi);
+inited=0;
+return;
+}
+
+#ifdef USE_ICONV
+/* 
+// zvbi-0.2.25/src/exp-txt.c skip debug message 
+*/
+
+/*
+ *  libzvbi - Text export functions
+ *
+ *  Copyright (C) 2001, 2002 Michael H. Schimek
+ *
+ *  Based on code from AleVT 1.5.1
+ *  Copyright (C) 1998, 1999 Edgar Toernig <froese@gmx.de>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* $Id: exp-txt.c,v 1.18 2006/05/31 03:55:52 mschimek Exp $ */
+
+static vbi_bool
+print_unicode(iconv_t cd, int endian, int unicode, char **p, int n)
+{
+	char in[2], *ip, *op;
+	size_t li, lo, r;
+
+	in[0 + endian] = unicode;
+	in[1 - endian] = unicode >> 8;
+	ip = in; op = *p;
+	li = sizeof(in); lo = n;
+
+	r = iconv(cd, &ip, &li, &op, &lo);
+
+	if ((size_t) -1 == r
+	    || (**p == 0x40 && unicode != 0x0040)) {
+		in[0 + endian] = 0x20;
+		in[1 - endian] = 0;
+		ip = in; op = *p;
+		li = sizeof(in); lo = n;
+
+		r = iconv(cd, &ip, &li, &op, &lo);
+
+		if ((size_t) -1 == r
+		    || (r == 1 && **p == 0x40))
+			goto error;
+	}
+
+	*p = op;
+
+	return TRUE;
+
+ error:
+	return FALSE;
+}
+
+int
+vbi_print_page_region_nodebug(vbi_page *pg, char *buf, int size,
+		      const char *format, vbi_bool table, vbi_bool rtl,
+		      int column, int row, int width, int height)
+{
+	int endian = vbi_ucs2be();
+	int column0, column1, row0, row1;
+	int x, y, spaces, doubleh, doubleh0;
+	iconv_t cd;
+	char *p;
+
+	rtl = rtl;
+
+	column0 = column;
+	row0 = row;
+	column1 = column + width - 1;
+	row1 = row + height - 1;
+
+	if (!pg || !buf || size < 0 || !format
+	    || column0 < 0 || column1 >= pg->columns
+	    || row0 < 0 || row1 >= pg->rows
+	    || endian < 0)
+		return 0;
+
+	if ((cd = iconv_open(format, "UCS-2")) == (iconv_t) -1)
+		return 0;
+
+	p = buf;
+
+	doubleh = 0;
+
+	for (y = row0; y <= row1; y++) {
+		int x0, x1, xl;
+
+		x0 = (table || y == row0) ? column0 : 0;
+		x1 = (table || y == row1) ? column1 : (pg->columns - 1);
+
+		xl = (table || y != row0 || (y + 1) != row1) ? -1 : column1;
+
+		doubleh0 = doubleh;
+
+		spaces = 0;
+		doubleh = 0;
+
+		for (x = x0; x <= x1; x++) {
+			vbi_char ac = pg->text[y * pg->columns + x];
+
+			if (table) {
+				if (ac.size > VBI_DOUBLE_SIZE)
+					ac.unicode = 0x0020;
+			} else {
+				switch (ac.size) {
+				case VBI_NORMAL_SIZE:
+				case VBI_DOUBLE_WIDTH:
+					break;
+
+				case VBI_DOUBLE_HEIGHT:
+				case VBI_DOUBLE_SIZE:
+					doubleh++;
+					break;
+
+				case VBI_OVER_TOP:
+				case VBI_OVER_BOTTOM:
+					continue;
+
+				case VBI_DOUBLE_HEIGHT2:
+				case VBI_DOUBLE_SIZE2:
+					if (y > row0)
+						ac.unicode = 0x0020;
+					break;
+				}
+
+				/*
+				 *  Special case two lines row0 ... row1, and all chars
+				 *  in row0, column0 ... column1 are double height: Skip
+				 *  row1, don't wrap around.
+				 */
+				if (x == xl && doubleh >= (x - x0)) {
+					x1 = xl;
+					y = row1;
+				}
+
+				if (ac.unicode == 0x20 || !vbi_is_print(ac.unicode)) {
+					spaces++;
+					continue;
+				} else {
+					if (spaces < (x - x0) || y == row0) {
+						for (; spaces > 0; spaces--)
+							if (!print_unicode(cd, endian, 0x0020,
+									   &p, buf + size - p))
+								goto failure;
+					} else /* discard leading spaces */
+						spaces = 0;
+				}
+			}
+
+			if (!print_unicode(cd, endian, ac.unicode, &p, buf + size - p))
+				goto failure;
+		}
+
+		/* if !table discard trailing spaces and blank lines */
+
+		if (y < row1) {
+			int left = buf + size - p;
+
+			if (left < 1)
+				goto failure;
+
+			if (table) {
+				*p++ = '\n'; /* XXX convert this (eg utf16) */
+			} else if (spaces >= (x1 - x0)) {
+				; /* suppress blank line */
+			} else {
+				/* exactly one space between adjacent rows */
+				if (!print_unicode(cd, endian, 0x0020, &p, left))
+					goto failure;
+			}
+		} else {
+			if (doubleh0 > 0) {
+				; /* prentend this is a blank double height lower row */
+			} else {
+				for (; spaces > 0; spaces--)
+					if (!print_unicode(cd, endian, 0x0020, &p, buf + size - p))
+						goto failure;
+			}
+		}
+	}
+
+	iconv_close(cd);
+	return p - buf;
+
+ failure:
+	iconv_close(cd);
+	return 0;
+}
+#endif
+
+
+/*
+// Decode event handler
+*/
+static void event_handler(vbi_event *ev, void *data)
+{
+priv_vbi_t *user_vbi = (priv_vbi_t*)data;
+vbi_page pg;
+char *s;
+int i;
+
+switch (ev->type) {
+    case VBI_EVENT_CAPTION:
+	printf("caption\n");
+	break;
+    case VBI_EVENT_NETWORK:
+	s=ev->ev.network.name;
+	if(s) {
+	    pthread_mutex_lock(&(user_vbi->buffer_mutex));
+	    user_vbi->network_name = strdup(s);
+	    pthread_mutex_unlock(&(user_vbi->buffer_mutex));
+	    }
+	break;
+    case VBI_EVENT_NETWORK_ID:
+	s=ev->ev.network.name;
+	if(s) {
+	    pthread_mutex_lock(&(user_vbi->buffer_mutex));
+	    user_vbi->network_id = strdup(s);
+	    pthread_mutex_unlock(&(user_vbi->buffer_mutex));
+	    }
+	break;
+    case VBI_EVENT_TTX_PAGE:
+	pthread_mutex_lock(&(user_vbi->buffer_mutex));
+	user_vbi->curr_pgno=ev->ev.ttx_page.pgno;	// page number
+	user_vbi->curr_subno=ev->ev.ttx_page.subno;	// subpage
+	char out[VBI_TXT_PAGE_SIZE];
+	memset(out,0,sizeof(out));
+	vbi_page page;
+	vbi_fetch_vt_page(user_vbi->decoder, 		// fetch page
+	    &(page),
+	    ev->ev.ttx_page.pgno,
+	    ev->ev.ttx_page.subno,
+	    VBI_WST_LEVEL_3p5,
+	    25,
+	    TRUE);
+	if(user_vbi->curr_pgno!=user_vbi->pgno || 	// drop page
+		user_vbi->curr_subno>VBI_MAX_SUBPAGES) {
+	    switch (user_vbi->tformat) {
+		case VBI_TFORMAT_TEXT: {
+#ifdef USE_ICONV
+		    vbi_print_page_region_nodebug(&(page),out,sizeof(out),VBI_TEXT_CHARSET,TRUE,TRUE,
+			0,0,page.columns, 1);
+#else
+		    vbi_print_page_region(&(page),out,sizeof(out),VBI_TEXT_CHARSET,TRUE,TRUE,
+			0,0,page.columns, 1);
+#endif
+		    char *p;
+		    if(NULL!=(p=strchr(out,'\n'))) *p=0;
+		    memset(user_vbi->header,0,sizeof(user_vbi->header));
+		    strncpy(user_vbi->header,out,sizeof(user_vbi->header));	// cut header
+		    break;
+		    }
+		case VBI_TFORMAT_BW:
+		case VBI_TFORMAT_GRAY:
+		case VBI_TFORMAT_COLOR: {
+		    if(user_vbi->redraw && user_vbi->on && user_vbi->canvas) {	// redraw last decoded page
+			user_vbi->spudec_proc=1;
+			user_vbi->redraw=0;
+			} else if(user_vbi->act_pgno!=user_vbi->pgno && user_vbi->on) {	// modify header
+			int csize = page.columns * page.rows * 12 * 10 * sizeof(vbi_rgba);
+			void *canvas=malloc(csize);
+			if(csize > user_vbi->canvas_size) {	// test canvas size
+			    if(user_vbi->canvas) free(user_vbi->canvas);
+			    user_vbi->canvas=malloc(csize);
+			    memset(user_vbi->canvas,0,csize);
+			    user_vbi->canvas_size=0;
+			    if(user_vbi->canvas) user_vbi->canvas_size=csize;
+			    }
+			vbi_draw_vt_page(&(page),		// vbi_page to RGBA32_LB image
+			    user_vbi->fmt,
+			    canvas,
+			    user_vbi->reveal, 
+			    user_vbi->flash_on);
+			int hsize = page.columns * 12 * 10 * sizeof(vbi_rgba);
+			memcpy(user_vbi->canvas,canvas,hsize);	// cut handler
+			user_vbi->spudec_proc=1;
+			user_vbi->csize=csize;
+			user_vbi->columns=page.columns;
+			user_vbi->rows=page.rows;
+			free(canvas);
+			}
+		    break;
+		    }
+		}
+	    pthread_mutex_unlock(&(user_vbi->buffer_mutex));
+	    return;
+	    }
+	if(!user_vbi->on) {
+	    pthread_mutex_unlock(&(user_vbi->buffer_mutex));
+	    return;
+	    }
+	// store page
+	user_vbi->valid_pages[user_vbi->curr_subno]=1;
+	user_vbi->act_subno=ev->ev.ttx_page.subno;
+	user_vbi->act_pgno=user_vbi->curr_pgno;
+	user_vbi->columns=page.columns;
+	user_vbi->rows=page.rows;
+	memcpy(&(user_vbi->pages[user_vbi->curr_subno]),&(page),sizeof(vbi_page));
+	switch (user_vbi->tformat) {
+	    case VBI_TFORMAT_TEXT: {		// mode: text
+		if(user_vbi->txtpages) {
+		    char *pagesptr;
+#ifdef USE_ICONV
+		    vbi_print_page_region_nodebug(&(page),out,sizeof(out),VBI_TEXT_CHARSET,TRUE,0,
+			    0,0,page.columns, page.rows);	// vbi_page to text without message
+#else
+		    vbi_print_page(&(page),out,sizeof(out),VBI_TEXT_CHARSET,TRUE,0);
+#endif
+		    pagesptr=user_vbi->txtpages+(user_vbi->curr_subno*VBI_TXT_PAGE_SIZE);
+		    memcpy(pagesptr,out,VBI_TXT_PAGE_SIZE);	// store page text
+		    }
+		break; }
+	    case VBI_TFORMAT_BW:		// mode: black & white
+	    case VBI_TFORMAT_GRAY:		// mode: grayscale
+	    case VBI_TFORMAT_COLOR: {		// mode: color (request color spu patch!)
+		int csize = page.columns * page.rows * 12 * 10 * sizeof(vbi_rgba);
+		void *canvas=malloc(csize);
+		if(csize > user_vbi->canvas_size) {	// test canvas size
+		    if(user_vbi->canvas) free(user_vbi->canvas);
+		    user_vbi->canvas=malloc(csize);
+		    user_vbi->canvas_size=0;
+		    if(user_vbi->canvas) user_vbi->canvas_size=csize;
+		    }
+		if(user_vbi->canvas) {
+		    vbi_draw_vt_page(&(page),
+			user_vbi->fmt,
+			canvas,
+			user_vbi->reveal, 
+			user_vbi->flash_on);
+		    memcpy(user_vbi->canvas,canvas,csize);	// store image
+		    user_vbi->csize=csize;
+		    }
+		if(user_vbi->on)
+		    user_vbi->spudec_proc=1;
+		free(canvas);
+		break; }
+	    }
+	pthread_mutex_unlock(&(user_vbi->buffer_mutex));
+	break;
+    }
+}
+
+/*
+// grab function
+*/
+static void *grabber(void *data)
+{
+priv_vbi_t *user_vbi = (priv_vbi_t*)data;
+vbi_capture_buffer *sliced_buffer;
+struct timeval timeout;
+unsigned int n_lines;
+int r,err_count = 0;
+
+while (!user_vbi->eof) {
+    timeout.tv_sec = 0;
+    timeout.tv_usec = 500;
+    r = vbi_capture_pull (user_vbi->capture, NULL, &sliced_buffer, &timeout);	// grab slices
+    if(user_vbi->eof) return NULL;
+    switch (r) {
+	case -1:	// read error
+	    if(err_count++>4) user_vbi->eof=1;
+	    break;
+	case 0: 	// time out
+	    break;
+	default:
+	    err_count=0;
+	}
+    if(r!=1) continue;
+    n_lines = sliced_buffer->size / sizeof (vbi_sliced);
+    vbi_decode (user_vbi->decoder,(vbi_sliced *) sliced_buffer->data,n_lines,sliced_buffer->timestamp);	// decode slice
+    }
+switch(r) {
+    case -1:
+        mp_msg (MSGT_STREAM, MSGL_ERR, "VBI read error %d (%s)\n",
+				 errno, strerror (errno));
+        return NULL;
+    case 0: 
+        mp_msg (MSGT_STREAM, MSGL_ERR, "VBI read timeout\n");
+	return NULL;
+    }
+return NULL;
+}
+
+/*
+// (HEX) ret = (HEX) curr + (DEC) direction
+*/
+int steppage(int curr,int direction)
+{
+int newpage;
+char num[5];
+
+snprintf(&num,4,"%03X",curr);
+newpage=atoi(num)+direction;
+if(newpage<100) newpage=100;
+if(newpage>999) newpage=999;
+snprintf(&num,4,"%03d",newpage);
+newpage=0;
+if(num[0]>='0' && num[0]<='9')
+    newpage+=(num[0]-'0')*256;
+if(num[1]>='0' && num[1]<='9')
+    newpage+=(num[1]-'0')*16;
+if(num[2]>='0' && num[2]<='9')
+    newpage+=(num[2]-'0');
+if(newpage<0x100) newpage=0x100;
+return newpage;
+}
+
+/*
+// init: device; text|bw|gray|color; first page number
+*/
+priv_vbi_t* vbi_init(char *tdevice, char *tformat, char *tpage)
+{
+if(inited) return NULL;
+unsigned int services =
+    VBI_SLICED_TELETEXT_B | \
+    VBI_SLICED_CAPTION_525 | \
+    VBI_SLICED_CAPTION_625 | \
+    VBI_SLICED_VBI_525 | \
+    VBI_SLICED_VBI_625 | \
+    VBI_SLICED_WSS_625 | \
+    VBI_SLICED_WSS_CPR1204 | \
+    VBI_SLICED_VPS;
+int formatid=VBI_TFORMAT_TEXT;	// default
+int startpage=0;
+if (tformat!=NULL) {
+    if (strcmp(tformat,"text")==0)
+	formatid=VBI_TFORMAT_TEXT;
+    if (strcmp(tformat,"bw")==0)
+	formatid=VBI_TFORMAT_BW;
+    if (strcmp(tformat,"gray")==0)
+	formatid=VBI_TFORMAT_GRAY;
+    if (strcmp(tformat,"color")==0)
+	formatid=VBI_TFORMAT_COLOR;
+    }
+if(tpage!=NULL)
+    startpage=steppage(0,atoi(tpage));	// page number is HEX
+if(startpage<0x100 || startpage>0x999)
+    startpage=0x100;
+return open_vbi(tdevice,services,formatid,startpage);
+}
+
+/*
+// uninit
+*/
+void vbi_uninit(priv_vbi_t* priv_vbi)
+{
+close_vbi(priv_vbi);
+}
+
+/*
+// Teletext on/off
+*/
+void vbi_switch(priv_vbi_t *priv_vbi, int flag)
+{
+pthread_mutex_lock(&(priv_vbi->buffer_mutex));
+switch (flag) {
+    case 0: priv_vbi->on=0; break;
+    case 1: priv_vbi->on=1; break;
+    default:
+    priv_vbi->on=!priv_vbi->on;
+    }
+if(!priv_vbi->on) {
+    priv_vbi->act_pgno=0;
+    priv_vbi->csize=0;
+    }
+priv_vbi->spudec_proc=1;
+pthread_mutex_unlock(&(priv_vbi->buffer_mutex));
+}
+
+/*
+// Teletext reset with change tv channel
+*/
+void vbi_reset(priv_vbi_t *priv_vbi)
+{
+pthread_mutex_lock(&(priv_vbi->buffer_mutex));
+if(priv_vbi->txtpages)
+    memset(priv_vbi->txtpages,0,VBI_MAX_SUBPAGES*VBI_TXT_PAGE_SIZE);
+memset(priv_vbi->pages,0,sizeof(priv_vbi->pages));
+memset(priv_vbi->valid_pages,0,sizeof(priv_vbi->valid_pages));
+snprintf(priv_vbi->header,sizeof(priv_vbi->header),"%s",VBI_NO_TELETEXT);
+if(priv_vbi->canvas) free(priv_vbi->canvas);
+priv_vbi->canvas=NULL;
+priv_vbi->canvas_size=0;
+priv_vbi->act_pgno=0;
+priv_vbi->redraw=1;
+priv_vbi->csize=0;
+priv_vbi->spudec_proc=1;
+pthread_mutex_unlock(&(priv_vbi->buffer_mutex));
+}
+
+/*
+// Set opacity display mode (only in SPU mode)
+*/
+void vbi_switch_alpha(priv_vbi_t *priv_vbi, int flag)
+{
+if(priv_vbi->tformat==VBI_TFORMAT_TEXT) return;
+pthread_mutex_lock(&(priv_vbi->buffer_mutex));
+switch (flag) {
+    case 0: priv_vbi->alpha=0; break;
+    case 1: priv_vbi->alpha=1; break;
+    default:
+    priv_vbi->alpha=!priv_vbi->alpha;
+    }
+if(priv_vbi->alpha)
+    priv_vbi->reveal=1;
+    else
+    priv_vbi->reveal=0;
+priv_vbi->redraw=1;
+pthread_mutex_unlock(&(priv_vbi->buffer_mutex));
+}
+
+/*
+// Set half page mode (only in SPU mode)
+// flag=0 : half mode off
+//	1 : top half page
+//	2 : bottom half page
+*/
+void vbi_switch_half(priv_vbi_t *priv_vbi, int flag)
+{
+if(priv_vbi->tformat==VBI_TFORMAT_TEXT) return;
+pthread_mutex_lock(&(priv_vbi->buffer_mutex));
+switch (flag) {
+    case 0: priv_vbi->half=0; break;	// off
+    case 1: priv_vbi->half=1; break;	// top
+    case 2: priv_vbi->half=2; break;	// bottom
+    default:
+    priv_vbi->half+=1;			// step
+    if(priv_vbi->half>2) priv_vbi->half=0;
+    }
+priv_vbi->redraw=1;
+pthread_mutex_unlock(&(priv_vbi->buffer_mutex));
+}
+
+/*
+// Set page number
+*/
+void vbi_setpage(priv_vbi_t *priv_vbi, int pgno, int subno)
+{
+pthread_mutex_lock(&(priv_vbi->buffer_mutex));
+priv_vbi->pgno=steppage(0,pgno);
+priv_vbi->subno=subno;
+priv_vbi->act_subno=0;
+memset(priv_vbi->pages,0,sizeof(priv_vbi->pages));
+memset(priv_vbi->valid_pages,0,sizeof(priv_vbi->valid_pages));
+pthread_mutex_unlock(&(priv_vbi->buffer_mutex));
+}
+
+/*
+// Increase or decrease page
+*/
+void vbi_steppage(priv_vbi_t *priv_vbi, int direction)
+{
+pthread_mutex_lock(&(priv_vbi->buffer_mutex));
+priv_vbi->pgno=steppage(priv_vbi->pgno,direction);
+priv_vbi->act_subno=0;
+memset(priv_vbi->pages,0,sizeof(priv_vbi->pages));
+memset(priv_vbi->valid_pages,0,sizeof(priv_vbi->valid_pages));
+pthread_mutex_unlock(&(priv_vbi->buffer_mutex));
+}
+
+/*
+// Get current vbi_page pointer
+*/
+vbi_page* vbi_getpage(priv_vbi_t *priv_vbi)
+{
+if(!priv_vbi->on) return NULL;
+vbi_page* page = NULL;
+pthread_mutex_lock(&(priv_vbi->buffer_mutex));
+if (priv_vbi->valid_pages[priv_vbi->act_subno])
+    if (page=malloc(sizeof(vbi_page)))
+	memcpy(page,&(priv_vbi->pages[priv_vbi->act_subno]),sizeof(vbi_page));
+pthread_mutex_unlock(&(priv_vbi->buffer_mutex));
+return page;
+}
+
+/*
+// Get current page text (only in text mode)
+*/
+char* vbi_getpagetext(priv_vbi_t *priv_vbi)
+{
+if(!priv_vbi->on) return NULL;
+if(priv_vbi->tformat!=VBI_TFORMAT_TEXT && priv_vbi->canvas) return NULL;
+char* page = NULL;
+pthread_mutex_lock(&(priv_vbi->buffer_mutex));
+if (priv_vbi->valid_pages[priv_vbi->act_subno])
+    page=priv_vbi->txtpages+(priv_vbi->act_subno*VBI_TXT_PAGE_SIZE);
+if(!page) page=priv_vbi->header;
+pthread_mutex_unlock(&(priv_vbi->buffer_mutex));
+return page;
+}
+
+/*
+// Get current page RGBA32 image (only in SPU mode)
+*/
+tv_teletext_img_t* vbi_getpageimg(priv_vbi_t *priv_vbi)
+{
+if(priv_vbi->tformat==VBI_TFORMAT_TEXT) return NULL;
+if(priv_vbi->spudec_proc==0) return NULL;
+tv_teletext_img_t* img = NULL;
+pthread_mutex_lock(&(priv_vbi->buffer_mutex));
+if (NULL!=(img=malloc(sizeof(tv_teletext_img_t)))) {
+    img->tformat=priv_vbi->tformat;	// format: bw|gray|color
+    img->alpha=priv_vbi->alpha;		// opacity mode
+    img->half=priv_vbi->half;		// half mode
+    img->columns=priv_vbi->columns;	// page size
+    img->rows=priv_vbi->rows;
+    img->canvas=NULL;
+    if(priv_vbi->canvas && priv_vbi->on && priv_vbi->csize) {	// page ok?
+	if(NULL!=(img->canvas=malloc(priv_vbi->csize)))
+	    memcpy(img->canvas,priv_vbi->canvas,priv_vbi->csize);
+	}    
+    }
+priv_vbi->spudec_proc=0;
+pthread_mutex_unlock(&(priv_vbi->buffer_mutex));
+return img;
+}
--- libvo/sub.h	 (revision 22772)
+++ libvo/sub.h	 (working copy)
@@ -2,6 +2,10 @@
 #ifndef __MPLAYER_SUB_H
 #define __MPLAYER_SUB_H
 
+#ifdef HAVE_TV_TELETEXT
+#include "libmpcodecs/mp_image.h"
+#endif
+
 typedef struct mp_osd_bbox_s {
     int x1,y1,x2,y2;
 } mp_osd_bbox_t;
@@ -11,6 +15,7 @@
 #define OSDTYPE_PROGBAR 3
 #define OSDTYPE_SPU 4
 #define OSDTYPE_DVDNAV 5
+#define OSDTYPE_TELETEXT 6
 
 #define OSDFLAG_VISIBLE 1
 #define OSDFLAG_CHANGED 2
@@ -64,6 +69,12 @@
 
 extern unsigned char* vo_osd_text;
 
+#ifdef HAVE_TV_TELETEXT
+extern unsigned char* vo_osd_teletex_text;
+extern int vo_osd_teletext_flip;
+extern mp_image_t* vo_osd_teletex_img;
+#endif
+
 extern int vo_osd_progbar_type;
 extern int vo_osd_progbar_value;   // 0..255
 
--- libvo/sub.c	 (revision 22772)
+++ libvo/sub.c	 (working copy)
@@ -65,6 +65,11 @@
 font_desc_t* vo_font=NULL;
 
 unsigned char* vo_osd_text=NULL;
+#ifdef HAVE_TV_TELETEXT
+unsigned char* vo_osd_teletex_text=NULL;
+mp_image_t *vo_osd_teletex_img=NULL;
+int vo_osd_teletext_flip = 0;
+#endif
 int sub_unicode=0;
 int sub_utf8=0;
 int sub_pos=100;
@@ -228,6 +233,106 @@
 }
 #endif
 
+#ifdef HAVE_TV_TELETEXT
+inline static void vo_update_text_teletext(mp_osd_obj_t *obj, int dxs, int dys)
+{
+char *p,*pe;
+char line[256];
+int h=0,w=0,i,c,w1,font,lines,endline;
+int x1,y1,x2,y2;
+unsigned char *t;
+
+obj->flags|=OSDFLAG_CHANGED|OSDFLAG_VISIBLE;
+
+if (vo_osd_teletex_text==NULL) {
+    obj->flags&=~OSDFLAG_VISIBLE;
+    return;
+    }
+p=vo_osd_teletex_text;
+lines=0;
+endline=0;
+do {	// calculate teletext size
+    memset(line,0,sizeof(line));
+    if(pe=strchr(p,'\n')) {
+	if(pe-p>sizeof(line))
+	    strncpy(line,p,sizeof(line));
+	    else
+	    strncpy(line,p,pe-p);}
+	else
+	strncpy(line,p,sizeof(line));
+
+    t=line; 
+    w1=0;
+    while (*t) {
+        c = utf8_get_char(&t);
+	if (!c) c++; // avoid UCS 0
+	render_one_glyph(vo_font, c);
+        w1+=vo_font->width[c]+vo_font->charspace;
+	}
+    h+=vo_font->height;
+    if(w1>w) w=w1;
+    if(pe) pe++;
+    p=pe;
+    lines++; 
+    if(h+vo_font->height*2>dys && endline==0) endline=lines;
+    } while (pe!=NULL);
+h=h+vo_font->height;
+w=w-vo_font->charspace;
+if (w>dxs) {x1=0; x2=dxs;} // calculate bbox size
+    else {x1=(dxs-w)/2; x2=x1+w+1;}
+if (h>dys) {y1=0; y2=dys;}
+    else {y1=0; y2=y1+h+1;}
+obj->bbox.x1 = obj->x = x1;
+obj->bbox.y1 = obj->y = y1;
+obj->bbox.x2 = x2;
+obj->bbox.y2 = y2;
+obj->flags |= OSDFLAG_BBOX;
+alloc_buf(obj);
+p=vo_osd_teletex_text;
+h=y1;
+if (vo_osd_teletext_flip)
+    endline=lines-endline;	// bottom page
+    else
+    endline=0;			// top page
+lines=0;
+do {	// show teletext page
+    memset(line,0,sizeof(line));
+    if(pe=strchr(p,'\n')) {
+	if(pe-p>sizeof(line))
+	    strncpy(line,p,sizeof(line));
+	    else
+	    strncpy(line,p,pe-p);}
+	else
+	strncpy(line,p,sizeof(line));
+
+    t=line; 
+
+    w1=x1;
+    if(lines==0 || endline==0 || lines>endline) {
+	while (*t) {
+    	    c = utf8_get_char(&t);
+	    if (!c) c++; // avoid UCS 0
+	    render_one_glyph(vo_font, c);
+	    if(w1+vo_font->width[c]>=x2) break;
+    	    if ((font=vo_font->font[c])>=0)
+        	draw_alpha_buf(obj,w1,h,
+            	    vo_font->width[c],
+            	    vo_font->pic_a[font]->h,
+            	    vo_font->pic_b[font]->bmp+vo_font->start[c],
+            	    vo_font->pic_a[font]->bmp+vo_font->start[c],
+            	    vo_font->pic_a[font]->w);
+    	    w1+=vo_font->width[c]+vo_font->charspace;
+	    }
+	h+=vo_font->height;
+	}
+    if(pe) pe++;
+    p=pe; 
+    if(h+vo_font->height*2>dys) pe=NULL;
+    lines++;
+    } while (pe!=NULL);
+}
+#endif
+
 int vo_osd_progbar_type=-1;
 int vo_osd_progbar_value=100;   // 0..256
 
@@ -852,6 +957,11 @@
 	case OSDTYPE_SUBTITLE:
 	    vo_update_text_sub(obj,dxs,dys);
 	    break;
+#ifdef HAVE_TV_TELETEXT
+	case OSDTYPE_TELETEXT:
+	    vo_update_text_teletext(obj,dxs,dys);
+	    break;
+#endif
 	case OSDTYPE_PROGBAR:
 	    vo_update_text_progbar(obj,dxs,dys);
 	    break;
@@ -919,6 +1029,9 @@
 #ifdef USE_DVDNAV
     new_osd_obj(OSDTYPE_DVDNAV);
 #endif
+#if HAVE_TV_TELETEXT
+    new_osd_obj(OSDTYPE_TELETEXT);
+#endif
 #ifdef HAVE_FREETYPE
     force_load_font = 1;
 #endif
@@ -957,6 +1070,9 @@
 #ifdef USE_DVDNAV
         case OSDTYPE_DVDNAV:
 #endif
+#ifdef HAVE_TV_TELETEXT
+        case OSDTYPE_TELETEXT:
+#endif
 	case OSDTYPE_OSD:
 	case OSDTYPE_SUBTITLE:
 	case OSDTYPE_PROGBAR:
--- input/input.h	 (revision 22772)
+++ input/input.h	 (working copy)
@@ -93,6 +93,12 @@
 #define MP_CMD_STEP_PROPERTY 91
 #define MP_CMD_RADIO_STEP_FREQ 92
 #define MP_CMD_TV_STEP_FREQ 93
+#define MP_CMD_VBI_SWITCH 93
+#define MP_CMD_VBI_SET_PAGE 94
+#define MP_CMD_VBI_STEP_PAGE 95
+#define MP_CMD_VBI_FLIP_PAGE 96
+#define MP_CMD_VBI_ALPHA_PAGE 97
+#define MP_CMD_VBI_HALF_PAGE 98
 
 #define MP_CMD_GUI_EVENTS       5000
 #define MP_CMD_GUI_LOADFILE     5001
--- input/input.c	 (revision 22772)
+++ input/input.c	 (working copy)
@@ -136,6 +136,14 @@
   { MP_CMD_LOADLIST, "loadlist", 1, { {MP_CMD_ARG_STRING, {0}}, {MP_CMD_ARG_INT,{0}}, {-1,{0}} } },
   { MP_CMD_RUN, "run", 1, { {MP_CMD_ARG_STRING,{0}}, {-1,{0}} } },
   { MP_CMD_VF_CHANGE_RECTANGLE, "change_rectangle", 2, { {MP_CMD_ARG_INT,{0}}, {MP_CMD_ARG_INT,{0}}, {-1,{0}}}},
+#ifdef HAVE_TV_TELETEXT
+  { MP_CMD_VBI_SWITCH, "vbi_switch", 0, { {-1,{0}} }  },
+  { MP_CMD_VBI_SET_PAGE, "vbi_set_page", 0, { { MP_CMD_ARG_INT,{-1} }, {-1,{0}} } },
+  { MP_CMD_VBI_STEP_PAGE, "vbi_step_page",1, { { MP_CMD_ARG_INT ,{0}}, { MP_CMD_ARG_INT ,{0}}, {-1,{0}} } },
+  { MP_CMD_VBI_FLIP_PAGE, "vbi_flip_page", 0, { {MP_CMD_ARG_INT,{-1}}, {-1,{0}} } },
+  { MP_CMD_VBI_ALPHA_PAGE, "vbi_alpha_page", 0, { {MP_CMD_ARG_INT,{-1}}, {-1,{0}} } },
+  { MP_CMD_VBI_HALF_PAGE, "vbi_half_page", 0, { {MP_CMD_ARG_INT,{-1}}, {-1,{0}} } },
+#endif
 
 #ifdef HAVE_NEW_GUI  
   { MP_CMD_GUI_LOADFILE, "gui_loadfile", 0, { {-1,{0}} } },
@@ -386,6 +394,14 @@
   { { 'n', 0 }, "tv_step_norm" },
   { { 'u', 0 }, "tv_step_chanlist" },
 #endif
+#ifdef HAVE_TV_TELETEXT
+  { { 'X', 0 }, "vbi_switch" },
+  { { 'P', 0 }, "vbi_flip_page" },
+  { { 'E', 0 }, "vbi_half_page" },
+  { { 'A', 0 }, "vbi_alpha_page" },
+  { { 'Q', 0 }, "vbi_step_page 1" },
+  { { 'W', 0 }, "vbi_step_page -1" },
+#endif
 #ifdef HAVE_JOYSTICK
   { { JOY_AXIS0_PLUS, 0 }, "seek 10" },
   { { JOY_AXIS0_MINUS, 0 }, "seek -10" },
