[MPlayer-cvslog] r23530 - in trunk: Changelog DOCS/man/en/mplayer.1 DOCS/tech/MAINTAINERS DOCS/tech/slave.txt cfg-common.h command.c configure input/input.c input/input.h libvo/sub.c libvo/sub.h mpcommon.c mplayer.c spudec.c spudec.h stream/Makefile stream/tv.c stream/tv.h stream/tvi_vbi.c stream/tvi_vbi.h

voroshil subversion at mplayerhq.hu
Sun Jun 10 02:06:12 CEST 2007


Author: voroshil
Date: Sun Jun 10 02:06:12 2007
New Revision: 23530

Log:
Teletext support for tv:// (v4l and v4l2 only)

modified patch from Otvos Attila oattila at chello dot hu



Added:
   trunk/stream/tvi_vbi.c   (contents, props changed)
   trunk/stream/tvi_vbi.h   (contents, props changed)
Modified:
   trunk/Changelog
   trunk/DOCS/tech/MAINTAINERS
   trunk/DOCS/tech/slave.txt
   trunk/cfg-common.h
   trunk/command.c
   trunk/configure
   trunk/input/input.c
   trunk/input/input.h
   trunk/libvo/sub.c
   trunk/libvo/sub.h
   trunk/mpcommon.c
   trunk/mplayer.c
   trunk/spudec.c
   trunk/spudec.h
   trunk/stream/Makefile
   trunk/stream/tv.c
   trunk/stream/tv.h

Changes in other areas also in this revision:
Modified:
   trunk/DOCS/man/en/mplayer.1

Modified: trunk/Changelog
==============================================================================
--- trunk/Changelog	(original)
+++ trunk/Changelog	Sun Jun 10 02:06:12 2007
@@ -51,6 +51,7 @@ MPlayer (1.0)
     * support H.264 over RTSP
     * "device" and "adevice" suboptions now works for *BSD BT848 tv driver too
     * dvdnav:// now depends on mplayer's fork of libdvdnav
+    * Teletext support for tv:// (v4l and v4l2 only)
 
     FFmpeg/libavcodec:
     * Intel Music coder audio decoder

Modified: trunk/DOCS/tech/MAINTAINERS
==============================================================================
--- trunk/DOCS/tech/MAINTAINERS	(original)
+++ trunk/DOCS/tech/MAINTAINERS	Sun Jun 10 02:06:12 2007
@@ -77,6 +77,7 @@ MPlayer code:
     * libmpdemux: Roberto Togni, Nico Sabbi
     * libmpcodecs: Roberto Togni
     * TV input/capture: Vladimir Voroshilov
+    * TV teletext: Vladimir Voroshilov
     * network streaming: Roberto Togni, Nico Sabbi, Benjamin Zores
     * DVD/VOB subtitles: None
     * config files & commandline parser: Alban Bedel

Modified: trunk/DOCS/tech/slave.txt
==============================================================================
--- trunk/DOCS/tech/slave.txt	(original)
+++ trunk/DOCS/tech/slave.txt	Sun Jun 10 02:06:12 2007
@@ -314,6 +314,19 @@ switch_vsync [value]
     Toggle vsync (1 == on, 0 == off). If [value] is not provided,
     vsync status is inverted.
 
+teletext_add_dec <value>
+    On/off teletext page number editing mode and append given digit to
+    previously entered one
+    0..9 - append apropriate digit (enables editing mode if called from normal mode, and
+           switches to normal mode when third digit is entered.
+    -    - delete last digit from page number (backspace amulation, works only in page number
+           editing mode)
+
+teletext_go_link <value>
+    Follow given links on current teletext page
+    0    - go to initial page (specified by -tv tpage= parameter)
+    1..6 - follow given link
+
 tv_step_channel <channel>
     Select next/previous TV channel.
 
@@ -446,4 +459,8 @@ tv_brightness      int       -100    100
 tv_contrast        int       -100    100     X   X   X
 tv_saturation      int       -100    100     X   X   X
 tv_hue             int       -100    100     X   X   X
-
+teletext_page      int       100     999     X   X   X
+teletext_mode      int       0       3       X   X   X    0 - off, 1 - opaque, 2 - transparent,
+                                                          3 - transparent inverted (bw format)
+teletext_format    int       0       3       X            0 - text, 1 - b/w, 2 - gray, 3 - color
+teletext_half_page int       0       2       X   X   X    0 - off, 1 - top half, 2- bottom half

Modified: trunk/cfg-common.h
==============================================================================
--- trunk/cfg-common.h	(original)
+++ trunk/cfg-common.h	Sun Jun 10 02:06:12 2007
@@ -460,6 +460,11 @@ m_option_t tvopts_conf[]={
 #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_INT, CONF_RANGE, 100, 999, NULL},
+#endif
 	{"audioid", &tv_param_audio_id, CONF_TYPE_INT, CONF_RANGE, 0, 9, NULL},
 	{NULL, NULL, 0, 0, 0, 0, NULL}
 };

Modified: trunk/command.c
==============================================================================
--- trunk/command.c	(original)
+++ trunk/command.c	Sun Jun 10 02:06:12 2007
@@ -1415,6 +1415,134 @@ static int mp_property_tv_color(m_option
 
 #endif
 
+#ifdef HAVE_TV_TELETEXT
+/// teletext page (RW)
+static int mp_property_teletext_page(m_option_t * prop, int action, void *arg,
+                  MPContext * mpctx)
+{
+    int val,result;
+    tvi_handle_t *tvh = mpctx->demuxer->priv;
+    if (mpctx->demuxer->type != DEMUXER_TYPE_TV || !tvh || !tvh->priv_vbi)
+        return M_PROPERTY_UNAVAILABLE;
+
+    switch (action) {
+    case M_PROPERTY_SET:
+        if (!arg)
+            return M_PROPERTY_ERROR;
+        M_PROPERTY_CLAMP(prop, *(int *) arg);
+        result=tv_teletext_control(tvh, TVI_CONTROL_VBI_SET_PAGE, arg);
+	break;
+    case M_PROPERTY_GET:
+        if (!arg)
+            return M_PROPERTY_ERROR;
+        result=tv_teletext_control(tvh, TVI_CONTROL_VBI_GET_PAGE, arg);
+	break;
+    case M_PROPERTY_STEP_UP:
+    case M_PROPERTY_STEP_DOWN:
+        val = (arg ? *(int *) arg : 1) * (action == M_PROPERTY_STEP_DOWN ? -1 : 1);
+        result=tv_teletext_control(tvh, TVI_CONTROL_VBI_STEP_PAGE, &val);
+	break;
+    default:
+        return M_PROPERTY_NOT_IMPLEMENTED;
+    }
+    return (result==TVI_CONTROL_TRUE?M_PROPERTY_OK:M_PROPERTY_ERROR);
+}
+/// VBI teletext mode (RW)
+static int mp_property_teletext_mode(m_option_t * prop, int action, void *arg,
+                  MPContext * mpctx)
+{
+    int val,result;
+    tvi_handle_t *tvh = mpctx->demuxer->priv;
+    if (mpctx->demuxer->type != DEMUXER_TYPE_TV || !tvh || !tvh->priv_vbi)
+        return M_PROPERTY_UNAVAILABLE;
+
+    switch (action) {
+    case M_PROPERTY_SET:
+        if (!arg)
+            return M_PROPERTY_ERROR;
+        M_PROPERTY_CLAMP(prop, *(int *) arg);
+        result=tv_teletext_control(tvh, TVI_CONTROL_VBI_SET_MODE, arg);
+	break;
+    case M_PROPERTY_GET:
+        if (!arg)
+            return M_PROPERTY_ERROR;
+        result=tv_teletext_control(tvh, TVI_CONTROL_VBI_GET_MODE, arg);
+	break;
+    case M_PROPERTY_STEP_UP:
+    case M_PROPERTY_STEP_DOWN:    
+        val = (arg ? *(int *) arg : 1) * (action == M_PROPERTY_STEP_DOWN ? -1 : 1);
+        result=tv_teletext_control(tvh, TVI_CONTROL_VBI_STEP_MODE, &val);
+	break;
+    default:
+        return M_PROPERTY_NOT_IMPLEMENTED;
+    }
+    return (result==TVI_CONTROL_TRUE?M_PROPERTY_OK:M_PROPERTY_ERROR);
+}
+/// VBI teletext format (R)
+static int mp_property_teletext_format(m_option_t * prop, int action, void *arg,
+                  MPContext * mpctx)
+{
+    int val,result;
+    tvi_handle_t *tvh = mpctx->demuxer->priv;
+    if (mpctx->demuxer->type != DEMUXER_TYPE_TV || !tvh || !tvh->priv_vbi)
+        return M_PROPERTY_UNAVAILABLE;
+
+    switch (action) {
+    case M_PROPERTY_GET:
+        if (!arg)
+            return M_PROPERTY_ERROR;
+        result=tv_teletext_control(tvh, TVI_CONTROL_VBI_GET_FORMAT, arg);
+	break;
+    case M_PROPERTY_SET:
+        if (!arg)
+            return M_PROPERTY_ERROR;
+        M_PROPERTY_CLAMP(prop, *(int *) arg);
+        result=tv_teletext_control(tvh, TVI_CONTROL_VBI_SET_FORMAT, arg);
+	break;
+    case M_PROPERTY_STEP_UP:
+    case M_PROPERTY_STEP_DOWN:
+        val = (arg ? *(int *) arg : 1) * (action == M_PROPERTY_STEP_DOWN ? -1 : 1);
+        result=tv_teletext_control(tvh, TVI_CONTROL_VBI_STEP_FORMAT, &val);
+	break;
+    default:
+        return M_PROPERTY_NOT_IMPLEMENTED;
+    }
+    return (result==TVI_CONTROL_TRUE?M_PROPERTY_OK:M_PROPERTY_ERROR);
+}
+
+/// VBI teletext half-page mode (RW)
+static int mp_property_teletext_half_page(m_option_t * prop, int action, void *arg,
+                  MPContext * mpctx)
+{
+    int val,result;
+    tvi_handle_t *tvh = mpctx->demuxer->priv;
+    if (mpctx->demuxer->type != DEMUXER_TYPE_TV || !tvh || !tvh->priv_vbi)
+        return M_PROPERTY_UNAVAILABLE;
+
+    switch (action) {
+    case M_PROPERTY_GET:
+        if (!arg)
+            return M_PROPERTY_ERROR;
+        result=tv_teletext_control(tvh, TVI_CONTROL_VBI_GET_HALF_PAGE, arg);
+	break;
+    case M_PROPERTY_SET:
+        if (!arg)
+            return M_PROPERTY_ERROR;
+        M_PROPERTY_CLAMP(prop, *(int *) arg);
+        result=tv_teletext_control(tvh, TVI_CONTROL_VBI_SET_HALF_PAGE, arg);
+	break;
+    case M_PROPERTY_STEP_UP:
+    case M_PROPERTY_STEP_DOWN:
+        val = (arg ? *(int *) arg : 1) * (action == M_PROPERTY_STEP_DOWN ? -1 : 1);
+        result=tv_teletext_control(tvh, TVI_CONTROL_VBI_STEP_HALF_PAGE, &val);
+	break;
+    default:
+        return M_PROPERTY_NOT_IMPLEMENTED;
+    }
+    return (result==TVI_CONTROL_TRUE?M_PROPERTY_OK:M_PROPERTY_ERROR);
+}
+#endif /* HAVE_TV_TELETEXT */
+
 ///@}
 
 /// All properties available in MPlayer.
@@ -1540,6 +1668,17 @@ static m_option_t mp_properties[] = {
      M_OPT_RANGE, -100, 100, (void *) TV_COLOR_HUE },
 #endif
 
+#ifdef HAVE_TV_TELETEXT
+    { "teletext_page", mp_property_teletext_page, CONF_TYPE_INT,
+     M_OPT_RANGE, -999, 999, NULL },
+    { "teletext_mode", mp_property_teletext_mode, CONF_TYPE_INT,
+     M_OPT_RANGE, 0, 3, NULL },
+    { "teletext_format", mp_property_teletext_format, CONF_TYPE_INT,
+     M_OPT_RANGE, 0, 3, NULL },
+    { "teletext_half_page", mp_property_teletext_half_page, CONF_TYPE_INT,
+     M_OPT_RANGE, 0, 2, NULL },
+#endif
+
     { NULL, NULL, NULL, 0, 0, 0, NULL }
 };
 
@@ -2233,6 +2372,16 @@ int run_command(MPContext * mpctx, mp_cm
 	    if (mpctx->file_format == DEMUXER_TYPE_TV)
 		tv_step_chanlist((tvi_handle_t *) (mpctx->demuxer->priv));
 	    break;
+#ifdef HAVE_TV_TELETEXT
+        case MP_CMD_TV_TELETEXT_ADD_DEC:
+            if (mpctx->file_format == DEMUXER_TYPE_TV)
+                tv_teletext_add_dec((tvi_handle_t *) (mpctx->demuxer->priv),cmd->args[0].v.s);
+            break;
+        case MP_CMD_TV_TELETEXT_GO_LINK:
+            if (mpctx->file_format == DEMUXER_TYPE_TV)
+                tv_teletext_go_link((tvi_handle_t *) (mpctx->demuxer->priv),cmd->args[0].v.i);
+            break;
+#endif /* HAVE_TV_TELETEXT */
 #endif				/* USE_TV */
 
 	case MP_CMD_SUB_LOAD:

Modified: trunk/configure
==============================================================================
--- trunk/configure	(original)
+++ trunk/configure	Sun Jun 10 02:06:12 2007
@@ -240,6 +240,7 @@ Optional features:
   --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]
@@ -588,6 +589,7 @@ _tv=yes
 _tv_v4l1=auto
 _tv_v4l2=auto
 _tv_bsdbt848=auto
+_tv_teletext=auto
 _pvr=auto
 _network=yes
 _winsock2=auto
@@ -935,6 +937,8 @@ for ac_option do
   --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	;;
@@ -6685,6 +6689,28 @@ else
 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
@@ -7546,6 +7572,7 @@ TV_V4L  = $_tv_v4l
 TV_V4L1 = $_tv_v4l1
 TV_V4L2 = $_tv_v4l2
 TV_BSDBT848 = $_tv_bsdbt848
+TV_TELETEXT = $_tv_teletext
 AUDIO_INPUT = $_audio_input
 PVR = $_pvr
 VCD = $_vcd
@@ -8098,6 +8125,9 @@ $_def_ioctl_bt848_h_name
 /* Enable *BSD BrookTree TV interface support */
 $_def_tv_bsdbt848
 
+/* Enable TV Teletext Interface support */
+$_def_tv_teletext
+
 /* Enable Radio Interface support */
 $_def_radio
 

Modified: trunk/input/input.c
==============================================================================
--- trunk/input/input.c	(original)
+++ trunk/input/input.c	Sun Jun 10 02:06:12 2007
@@ -136,6 +136,10 @@ static mp_cmd_t mp_cmds[] = {
   { 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_TV_TELETEXT_ADD_DEC, "teletext_add_dec", 1, { {MP_CMD_ARG_STRING,{0}}, {-1,{0}} } },
+  { MP_CMD_TV_TELETEXT_GO_LINK, "teletext_go_link",1, { { MP_CMD_ARG_INT ,{0}}, { MP_CMD_ARG_INT ,{0}}, {-1,{0}} } },
+#endif
 
 #ifdef HAVE_NEW_GUI  
   { MP_CMD_GUI_LOADFILE, "gui_loadfile", 0, { {-1,{0}} } },
@@ -386,6 +390,12 @@ static mp_cmd_bind_t def_cmd_binds[] = {
   { { 'n', 0 }, "tv_step_norm" },
   { { 'u', 0 }, "tv_step_chanlist" },
 #endif
+#ifdef HAVE_TV_TELETEXT
+  { { 'X', 0 }, "step_property teletext_mode 1" },
+  { { 'E', 0 }, "step_property teletext_half_page 1" },
+  { { 'W', 0 }, "step_property teletext_page 1" },
+  { { 'Q', 0 }, "step_property teletext_page -1" },
+#endif
 #ifdef HAVE_JOYSTICK
   { { JOY_AXIS0_PLUS, 0 }, "seek 10" },
   { { JOY_AXIS0_MINUS, 0 }, "seek -10" },

Modified: trunk/input/input.h
==============================================================================
--- trunk/input/input.h	(original)
+++ trunk/input/input.h	Sun Jun 10 02:06:12 2007
@@ -93,6 +93,8 @@
 #define MP_CMD_STEP_PROPERTY 91
 #define MP_CMD_RADIO_STEP_FREQ 92
 #define MP_CMD_TV_STEP_FREQ 93
+#define MP_CMD_TV_TELETEXT_ADD_DEC 94
+#define MP_CMD_TV_TELETEXT_GO_LINK 95
 
 #define MP_CMD_GUI_EVENTS       5000
 #define MP_CMD_GUI_LOADFILE     5001

Modified: trunk/libvo/sub.c
==============================================================================
--- trunk/libvo/sub.c	(original)
+++ trunk/libvo/sub.c	Sun Jun 10 02:06:12 2007
@@ -67,6 +67,10 @@ font_desc_t* vo_font=NULL;
 font_desc_t* sub_font=NULL;
 
 unsigned char* vo_osd_text=NULL;
+#ifdef HAVE_TV_TELETEXT
+unsigned char* vo_osd_teletex_text=NULL;
+int vo_osd_teletext_flip = 0;
+#endif
 int sub_unicode=0;
 int sub_utf8=0;
 int sub_pos=100;
@@ -229,6 +233,121 @@ inline static void vo_update_nav (mp_osd
 }
 #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){
+        // calculate bbox size
+        x1=0; 
+        x2=dxs;
+    } 
+    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
 
@@ -859,6 +978,11 @@ int vo_update_osd(int dxs,int dys){
 	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;
@@ -926,6 +1050,9 @@ void vo_init_osd(void){
 #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
@@ -964,6 +1091,9 @@ void vo_draw_text(int dxs,int dys,void (
 #ifdef USE_DVDNAV
         case OSDTYPE_DVDNAV:
 #endif
+#ifdef HAVE_TV_TELETEXT
+        case OSDTYPE_TELETEXT:
+#endif
 	case OSDTYPE_OSD:
 	case OSDTYPE_SUBTITLE:
 	case OSDTYPE_PROGBAR:

Modified: trunk/libvo/sub.h
==============================================================================
--- trunk/libvo/sub.h	(original)
+++ trunk/libvo/sub.h	Sun Jun 10 02:06:12 2007
@@ -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 @@ typedef struct mp_osd_bbox_s {
 #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,11 @@ extern subtitle* vo_sub;
 
 extern unsigned char* vo_osd_text;
 
+#ifdef HAVE_TV_TELETEXT
+extern unsigned char* vo_osd_teletex_text;
+extern int vo_osd_teletext_flip;
+#endif
+
 extern int vo_osd_progbar_type;
 extern int vo_osd_progbar_value;   // 0..255
 

Modified: trunk/mpcommon.c
==============================================================================
--- trunk/mpcommon.c	(original)
+++ trunk/mpcommon.c	Sun Jun 10 02:06:12 2007
@@ -7,6 +7,9 @@
 #include "libvo/video_out.h"
 #include "spudec.h"
 #include "vobsub.h"
+#ifdef HAVE_TV_TELETEXT
+#include "stream/tv.h"
+#endif
 
 double sub_last_pts = -303;
 
@@ -138,3 +141,31 @@ void update_subtitles(sh_video_t *sh_vid
     }
     current_module=NULL;
 }
+
+void update_teletext(sh_video_t *sh_video, demuxer_t *demuxer, int reset)
+{
+#ifdef HAVE_TV_TELETEXT
+    int half_page;
+    tvi_handle_t *tvh = demuxer->priv;
+    if (demuxer->type != DEMUXER_TYPE_TV) return;
+    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);
+            vo_osd_teletex_text=NULL;
+            vo_osd_changed(OSDTYPE_TELETEXT);
+            return;
+        }
+        vo_osd_changed(OSDTYPE_SPU);
+    }
+    vo_osd_teletex_text=tv_get_teletext_txtpage(tvh);
+    tv_teletext_control(tvh,TVI_CONTROL_VBI_GET_HALF_PAGE,&half_page);
+    vo_osd_teletext_flip=half_page;
+    vo_osd_changed(OSDTYPE_TELETEXT);
+#endif
+}

Modified: trunk/mplayer.c
==============================================================================
--- trunk/mplayer.c	(original)
+++ trunk/mplayer.c	Sun Jun 10 02:06:12 2007
@@ -1040,6 +1040,10 @@ void init_vo_spudec(void) {
     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;
 }
@@ -1622,6 +1626,7 @@ static int generate_video_frame(sh_video
 	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))
@@ -2036,6 +2041,7 @@ static double update_video(int *blit_fra
 	    ++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,
@@ -2249,6 +2255,7 @@ static int seek(MPContext *mpctx, double
 	// 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) {
@@ -3123,7 +3130,11 @@ demux_info_print(mpctx->demuxer);
 
 //================== 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();
 }
 

Modified: trunk/spudec.c
==============================================================================
--- trunk/spudec.c	(original)
+++ trunk/spudec.c	Sun Jun 10 02:06:12 2007
@@ -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)
@@ -1185,3 +1188,115 @@ void spudec_set_hw_spu(void *this, vo_fu
   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;
+}
+
+/// correction u and v planes half size
+#define SPU_DOUBLE_SIZE    1
+
+void alloc_images(spudec_handle_t* spu, int cmode) {
+
+    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;
+        }
+    }
+}
+
+/**
+    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;
+    unsigned char *aptr;
+    int h1 = 10;
+    int hs = 0;
+
+    if(!spu || !img)
+        return;
+    if(img->canvas==NULL) {
+        spudec_reset(spu);
+        if (spu->image)
+            free(spu->image);
+        spu->image=NULL;
+        spu->image_size = 0;
+        return;
+    }
+
+    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);
+    memset(spu->image,0,spu->image_size*2);
+
+    for(py=0;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
+            if(grey<=0x10) grey=0;
+            alpha=VBI_A(*pin);
+            switch (img->tformat) {
+                case 0x01:    // BW
+                case 0x02:    // Gray
+                case 0x03:    // Color (not supported)
+                    alpha=0x100-alpha;
+                    if (grey + alpha > 255) grey = 256 - alpha;
+                    break;
+            }
+            *iptr=grey;    // store Y plane
+            *aptr=alpha;   // store alpha
+            iptr++;
+            aptr++;
+            pin++;
+        }
+    }
+    spu->start_pts=0;
+    spu->end_pts=UINT_MAX;
+}
+#endif

Modified: trunk/spudec.h
==============================================================================
--- trunk/spudec.h	(original)
+++ trunk/spudec.h	Sun Jun 10 02:06:12 2007
@@ -20,5 +20,8 @@ int spudec_changed(void *this);
 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, 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
 

Modified: trunk/stream/Makefile
==============================================================================
--- trunk/stream/Makefile	(original)
+++ trunk/stream/Makefile	Sun Jun 10 02:06:12 2007
@@ -51,6 +51,7 @@ SRCS_COMMON-$(TV)                += stre
 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
 

Modified: trunk/stream/tv.c
==============================================================================
--- trunk/stream/tv.c	(original)
+++ trunk/stream/tv.c	Sun Jun 10 02:06:12 2007
@@ -34,6 +34,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;
@@ -518,6 +522,9 @@ static demuxer_t* demux_open_tv(demuxer_
 	tv_uninit(tvh);
 	return NULL;
     }
+#ifdef HAVE_TV_TELETEXT
+    if(tvh->priv_vbi) teletext_control(tvh->priv_vbi,TVI_CONTROL_VBI_START,1); //arg must be not null
+#endif
     funcs = tvh->functions;
     demuxer->priv=tvh;
     
@@ -657,6 +664,12 @@ no_audio:
 static void demux_close_tv(demuxer_t *demuxer)
 {
     tvi_handle_t *tvh=(tvi_handle_t*)(demuxer->priv);
+#ifdef HAVE_TV_TELETEXT
+    if(tvh->priv_vbi) {
+        teletext_uninit(tvh->priv_vbi);
+        tvh->priv_vbi=NULL;
+    }
+#endif
     if (!tvh) return;
     tvh->functions->uninit(tvh->priv);
     demuxer->priv=NULL;
@@ -688,6 +701,10 @@ tvi_handle_t *tv_begin(void)
             tvi_driver_list[i]->name,
             tvi_driver_list[i]->author,
             tvi_driver_list[i]->comment?tvi_driver_list[i]->comment:"");
+#ifdef HAVE_TV_TELETEXT
+            h->priv_vbi=teletext_init();
+#endif
+
             return h;
         }
     }
@@ -773,6 +790,9 @@ int tv_set_freq(tvi_handle_t *tvh, unsig
 	mp_msg(MSGT_TV, MSGL_V, MSGTR_TV_CurrentFrequency,
 	    freq, (float)freq/16);
     }
+#ifdef HAVE_TV_TELETEXT
+    if (tvh->priv_vbi) teletext_control(tvh->priv_vbi,TVI_CONTROL_VBI_RESET,1); //arg must be not null
+#endif
     return(1);
 }
 
@@ -932,6 +952,9 @@ int tv_step_norm(tvi_handle_t *tvh)
       return 0;
     }
   }
+#ifdef HAVE_TV_TELETEXT
+    if (tvh->priv_vbi) teletext_control(tvh->priv_vbi,TVI_CONTROL_VBI_RESET,1); //arg must be not null
+#endif
     return(1);
 }
 
@@ -949,9 +972,61 @@ int tv_set_norm(tvi_handle_t *tvh, char*
 	mp_msg(MSGT_TV, MSGL_ERR, MSGTR_TV_CannotSetNorm);
 	return 0;
     }
+#ifdef HAVE_TV_TELETEXT
+    if (tvh->priv_vbi) teletext_control(tvh->priv_vbi,TVI_CONTROL_VBI_RESET,1); //arg must be not null
+#endif
     return(1);
 }
 
+#ifdef HAVE_TV_TELETEXT
+int tv_teletext_control(tvi_handle_t* tvh, int control, void* arg)
+{
+    if (!tvh || !tvh->priv_vbi)
+        return TVI_CONTROL_FALSE;
+
+    return teletext_control(tvh->priv_vbi,control,arg);
+}
+
+int tv_teletext_add_dec(tvi_handle_t *tvh, char *dec)
+{
+    if(!dec) return 0;
+    if (teletext_control(tvh->priv_vbi, TVI_CONTROL_VBI_ADD_DEC, &dec)!= TVI_CONTROL_TRUE)
+        return 0;
+    return 1;
+}
+
+int tv_teletext_go_link(tvi_handle_t *tvh, int linkno)
+{
+    if (teletext_control(tvh->priv_vbi, TVI_CONTROL_VBI_GO_LINK, &linkno)!= TVI_CONTROL_TRUE)
+        return 0;
+    return 1;
+}
+
+void* tv_get_teletext_vbipage(tvi_handle_t *tvh)
+{
+    void* page = NULL;
+    if (teletext_control(tvh->priv_vbi, TVI_CONTROL_VBI_GET_VBIPAGE, &page)!= TVI_CONTROL_TRUE)
+        return NULL;
+    return page;
+}
+
+char* tv_get_teletext_txtpage(tvi_handle_t *tvh)
+{
+    char* page = NULL;
+    if (teletext_control(tvh->priv_vbi, 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 (teletext_control(tvh->priv_vbi, 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",

Modified: trunk/stream/tv.h
==============================================================================
--- trunk/stream/tv.h	(original)
+++ trunk/stream/tv.h	Sun Jun 10 02:06:12 2007
@@ -43,6 +43,11 @@ extern int tv_param_alsa;
 #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 int tv_param_tpage;            ///< page number
+#endif
 extern int tv_param_brightness;
 extern int tv_param_contrast;
 extern int tv_param_hue;
@@ -72,6 +77,9 @@ typedef struct tvi_functions_s
 typedef struct tvi_handle_s {
     tvi_functions_t	*functions;
     void		*priv;
+#ifdef HAVE_TV_TELETEXT
+    void                *priv_vbi;
+#endif
     int 		seq;
 
     /* specific */
@@ -90,6 +98,18 @@ typedef struct tv_channels_s {
     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        height;
+    int        width;
+    int        half;
+} 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;
@@ -153,6 +173,31 @@ extern char *tv_channel_last_real;
 #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 */
 
+/* TELETEXT controls */
+#define TVI_CONTROL_VBI_SET_MODE        0x501   ///< on/off grab teletext
+#define TVI_CONTROL_VBI_GET_MODE        0x502   ///< get current mode teletext
+#define TVI_CONTROL_VBI_STEP_MODE       0x503  ///< step teletext mode
+
+#define TVI_CONTROL_VBI_SET_PAGE        0x504   ///< set grab teletext page number
+#define TVI_CONTROL_VBI_STEP_PAGE       0x505   ///< step grab teletext page number
+#define TVI_CONTROL_VBI_GET_PAGE        0x506   ///< get grabbed teletext page
+
+#define TVI_CONTROL_VBI_SET_FORMAT      0x507   ///< set teletext format
+#define TVI_CONTROL_VBI_STEP_FORMAT     0x508   ///< step teletext format
+#define TVI_CONTROL_VBI_GET_FORMAT      0x509   ///< get eletext format
+
+#define TVI_CONTROL_VBI_GET_HALF_PAGE   0x50a   ///< get current half page
+#define TVI_CONTROL_VBI_STEP_HALF_PAGE  0x50b   ///< switch half page
+#define TVI_CONTROL_VBI_SET_HALF_PAGE   0x50c   ///< switch half page
+
+#define TVI_CONTROL_VBI_ADD_DEC         0x50d   ///< add page number with dec
+#define TVI_CONTROL_VBI_GO_LINK         0x50e   ///< go link (1..6)
+#define TVI_CONTROL_VBI_GET_TXTPAGE     0x50f   ///< get grabbed text teletext page
+#define TVI_CONTROL_VBI_GET_IMGPAGE     0x510   ///< get grabbed image teletext page
+#define TVI_CONTROL_VBI_GET_VBIPAGE     0x511   ///< get vbi_image for grabbed teletext page
+#define TVI_CONTROL_VBI_RESET           0x512   ///< vbi reset
+#define TVI_CONTROL_VBI_START           0x513   ///< vbi start
+
 extern tvi_handle_t *tv_begin(void);
 extern int tv_init(tvi_handle_t *tvh);
 extern int tv_uninit(tvi_handle_t *tvh);
@@ -183,6 +228,20 @@ int tv_step_freq(tvi_handle_t *tvh, floa
 
 int tv_set_norm(tvi_handle_t *tvh, char* norm);
 
+#ifdef HAVE_TV_TELETEXT
+int tv_teletext_control(tvi_handle_t* tvh, int control,void* arg);
+/// add dec to pageno
+int tv_teletext_add_dec(tvi_handle_t *tvh, char *dec);
+/// go link
+int tv_teletext_go_link(tvi_handle_t *tvh, int linkno);
+/// get current vbi_page
+void* tv_get_teletext_vbipage(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

Added: trunk/stream/tvi_vbi.c
==============================================================================
--- (empty file)
+++ trunk/stream/tvi_vbi.c	Sun Jun 10 02:06:12 2007
@@ -0,0 +1,1197 @@
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "tv.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"
+
+char* tv_param_tdevice=NULL;        ///< teletext vbi device
+char* tv_param_tformat="gray";      ///< format: text,bw,gray,color
+int tv_param_tpage=100;             ///< page number
+
+
+#ifdef USE_ICONV
+/*
+------------------------------------------------------------------
+    zvbi-0.2.25/src/exp-txt.c skip debug "if(1) fprintf(stderr,) " 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 at 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$ **/
+
+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;
+}
+
+static 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;
+
+#if 0
+    if (1)
+        fprintf (stderr, "vbi_print_page_region '%s' "
+            "table=%d col=%d row=%d width=%d height=%d\n",
+             format, table, column, row, width, height);
+#endif
+
+    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
+/*
+ end of zvbi-0.2.25/src/exp-txt.c part
+*/
+
+
+/*
+------------------------------------------------------------------
+  Private routines
+------------------------------------------------------------------
+*/
+
+/**
+ * \brief Decode event handler
+ * \param ev VBI event
+ * \param data pointer to user defined data
+ *
+ */
+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:
+        mp_msg(MSGT_TV,MSGL_DBG3,"caption\n");
+        break;
+    case VBI_EVENT_NETWORK:
+        s = ev->ev.network.name;
+        if (s) {
+            pthread_mutex_lock(&(user_vbi->buffer_mutex));
+            if (user_vbi->network_name)
+                free(user_vbi->network_name);
+            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));
+            if (user_vbi->network_id)
+                free(user_vbi->network_id);
+            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
+        i = vbi_bcd2dec(ev->ev.ttx_page.pgno);
+        if (i > 0 && i < 1000) {
+            if (!user_vbi->cache[i])
+                user_vbi->cache[i] = (vbi_page *) malloc(sizeof(vbi_page));
+            vbi_fetch_vt_page(user_vbi->decoder,        // fetch page
+                              user_vbi->cache[i],
+                              ev->ev.ttx_page.pgno,
+                              ev->ev.ttx_page.subno,
+                              VBI_WST_LEVEL_3p5, 25, TRUE);
+            memcpy(user_vbi->theader, user_vbi->cache[i]->text,
+                sizeof(user_vbi->theader));
+        }
+        pthread_mutex_unlock(&(user_vbi->buffer_mutex));
+        break;
+        }
+}
+
+/**
+ * \brief Prepares page to be shown on screen
+ * \param priv_vbi private data structure
+ *
+ * This routine adds page number, current time, etc to page header
+ *
+ */
+static void process_page(priv_vbi_t * priv_vbi)
+{
+    char *pagesptr;
+    int csize, i, j, subtitle = 0, sflg, send;
+    void *canvas;
+    char cpage[5];
+    vbi_page page;
+
+    memcpy(&(page), priv_vbi->page, sizeof(vbi_page));
+    if (priv_vbi->pgno != priv_vbi->page->pgno) {
+        //don't clear first line
+        for (i = page.columns; i < 1056; i++) {
+            page.text[i].unicode = ' ';
+            page.text[i].background = VBI_TRANSPARENT_COLOR;
+        }
+        snprintf(cpage, sizeof(cpage), "%03X", priv_vbi->pgno);
+        page.text[1].unicode = cpage[0];
+        page.text[2].unicode = cpage[1];
+        page.text[3].unicode = cpage[2];
+        page.text[4].unicode = ' ';
+        page.text[5].unicode = ' ';
+        page.text[6].unicode = ' ';
+    }
+
+    //background page number & title
+    j=vbi_bcd2dec(priv_vbi->curr_pgno);
+    if (j>0 && j<1000 && priv_vbi->cache[j]){
+        for(i=8;i<priv_vbi->cache[j]->columns;i++){
+            page.text[i].unicode = priv_vbi->cache[j]->text[i].unicode;
+        }
+    }
+
+    if (page.text[1].unicode == ' ' && page.text[2].unicode == ' ' &&
+        page.text[3].unicode == ' ' && page.text[4].unicode == ' ' &&
+        page.text[5].unicode == ' ' && page.text[5].unicode == ' '
+        && !priv_vbi->half)
+        subtitle = 1;           // subtitle page
+    if (priv_vbi->pagenumdec) {
+        i = (priv_vbi->pagenumdec >> 12) & 0xf;
+        switch (i) {
+        case 1:
+            page.text[1].unicode = '0' + ((priv_vbi->pagenumdec >> 0) & 0xf);
+            page.text[2].unicode = '-';
+            page.text[3].unicode = '-';
+            break;
+        case 2:
+            page.text[1].unicode = '0' + ((priv_vbi->pagenumdec >> 4) & 0xf);
+            page.text[2].unicode = '0' + ((priv_vbi->pagenumdec >> 0) & 0xf);
+            page.text[3].unicode = '-';
+            break;
+        }
+        page.text[4].unicode = ' ';
+        page.text[5].unicode = ' ';
+        page.text[6].unicode = ' ';
+        page.text[1].foreground = VBI_WHITE;
+        page.text[2].foreground = VBI_WHITE;
+        page.text[3].foreground = VBI_WHITE;
+    }
+    priv_vbi->columns = page.columns;
+    priv_vbi->rows = page.rows;
+    if (!subtitle) {       // update time in header
+        memcpy(&(page.text[VBI_TIME_LINEPOS]),
+               &(priv_vbi->theader[VBI_TIME_LINEPOS]),
+               sizeof(vbi_char) * (priv_vbi->columns - VBI_TIME_LINEPOS));
+    }
+    switch (priv_vbi->tformat) {
+    case VBI_TFORMAT_TEXT:        // mode: text
+        if (priv_vbi->txtpage) {
+#ifdef USE_ICONV
+            vbi_print_page_region_nodebug(&(page), priv_vbi->txtpage, 
+                VBI_TXT_PAGE_SIZE, VBI_TEXT_CHARSET, TRUE, 
+                0, 0, 0, page.columns, page.rows);      // vbi_page to text without message
+#else
+            vbi_print_page(&(page), priv_vbi->txtpage, 
+                VBI_TXT_PAGE_SIZE, VBI_TEXT_CHARSET, TRUE, 0);
+#endif
+        }
+        priv_vbi->valid_page = 1;
+        break;
+    case VBI_TFORMAT_BW:        // mode: black & white
+        for (i=0; i < (priv_vbi->pgno!=page.pgno?page.columns:1056); i++) {
+            if (priv_vbi->foreground){
+                page.text[i].foreground = VBI_BLACK;
+                page.text[i].background = VBI_WHITE;
+	    }else{
+                page.text[i].foreground = VBI_WHITE;
+                page.text[i].background = VBI_BLACK;
+	    }
+        }
+    case VBI_TFORMAT_GRAY:        // mode: grayscale
+    case VBI_TFORMAT_COLOR:       // mode: color (request color spu patch!)
+
+
+
+        page.color_map[VBI_TRANSPARENT_COLOR] = 0;
+        if (priv_vbi->alpha) {
+            if (subtitle) {
+                for (i = 0; i < page.rows; i++) {
+                    sflg = 0;
+                    send = 0;
+                    for (j = 0; j < page.columns; j++) {
+                        if (page.text[i * page.columns + j].unicode != ' ') {
+                            sflg = 1;
+                            send = j;
+                        }
+                        if (sflg == 0)
+                            page.text[i * page.columns + j].background =
+                                VBI_TRANSPARENT_COLOR;
+                    }
+                    for (j = send + 1; j < page.columns; j++)
+                        page.text[i * page.columns + j].background =
+                            VBI_TRANSPARENT_COLOR;
+                }
+            } else {
+                for (i = 0; i < 1056; i++)
+                    page.text[i].background = VBI_TRANSPARENT_COLOR;
+            }
+        }
+        csize = page.columns * page.rows * 12 * 10 * sizeof(vbi_rgba);
+        if (csize == 0)
+            break;
+        if (csize > priv_vbi->canvas_size) {        // test canvas size
+            if (priv_vbi->canvas)
+                free(priv_vbi->canvas);
+            priv_vbi->canvas = malloc(csize);
+            priv_vbi->canvas_size = 0;
+            if (priv_vbi->canvas)
+                priv_vbi->canvas_size = csize;
+        }
+        if (priv_vbi->canvas) {
+            vbi_draw_vt_page(&(page),
+                             priv_vbi->fmt,
+                             priv_vbi->canvas,
+                             priv_vbi->reveal, priv_vbi->flash_on);
+            priv_vbi->csize = csize;
+        }
+        priv_vbi->spudec_proc = 1;
+        priv_vbi->valid_page = 1;
+        break;
+    }
+}
+
+/**
+ * \brief Update page in cache
+ * \param priv_vbi private data structure
+ *
+ * Routine also calls process_page to refresh currently visible page (if so)
+ * every time it was received from VBI by background thread.
+ *
+ */
+static void update_page(priv_vbi_t * priv_vbi)
+{
+    int i;
+    int index;
+    pthread_mutex_lock(&(priv_vbi->buffer_mutex));
+    /*
+       priv_vbi->redraw=1   - page redraw requested
+       pgno!=page->pgno     - page was switched
+       curr_pgno==pgno      - backgound process just fetched current page, refresh it
+     */
+    if (priv_vbi->redraw ||
+        priv_vbi->pgno != priv_vbi->page->pgno ||
+        priv_vbi->curr_pgno == priv_vbi->pgno) {
+        index = vbi_bcd2dec(priv_vbi->pgno);
+        if ( index <= 0 || index > 999 || !priv_vbi->cache[index]) {
+            // curr_pgno is last decoded page
+            index = vbi_bcd2dec(priv_vbi->curr_pgno);
+        }
+
+	if (index <=0 || index >999 || !priv_vbi->cache[index]){
+            priv_vbi->valid_page = 0;
+            memset(priv_vbi->page, 0, sizeof(vbi_page));
+	}else
+	{
+            memcpy(priv_vbi->page, priv_vbi->cache[index], sizeof(vbi_page));
+            process_page(priv_vbi);//prepare page to be shown on screen
+	}
+    }
+    pthread_mutex_unlock(&(priv_vbi->buffer_mutex));
+}
+
+/**
+ * \brief background grabber routine
+ * \param data user-defined data
+ *
+ */
+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
+        update_page(user_vbi);
+    }
+    switch (r) {
+    case -1:
+        mp_msg(MSGT_TV, MSGL_ERR, "VBI read error %d (%s)\n",
+               errno, strerror(errno));
+        return NULL;
+    case 0:
+        mp_msg(MSGT_TV, MSGL_ERR, "VBI read timeout\n");
+        return NULL;
+    }
+    return NULL;
+}
+
+/**
+ * \brief calculate increased/decreased by given value page number
+ * \param curr  current page number in hexadecimal for
+ * \param direction decimal value (can be negative) to add to value or curr parameter
+ * \return new page number in hexadecimal form
+ *
+ * VBI page numbers are represented in special hexadecimal form, e.g.
+ * page with number 123 (as seen by user) internally has number 0x123.
+ * and equation 0x123+8 should be equal to 0x131 instead of regular 0x12b.
+ * Page numbers 0xYYY (where Y is not belongs to (0..9) and pages below 0x100 and
+ * higher 0x999 are reserved for internal use.
+ *
+ */
+static int steppage(int curr, int direction)
+{
+    int newpage = vbi_dec2bcd(vbi_bcd2dec(curr) + direction);
+    if (newpage < 0x100)
+        newpage = 0x100;
+    if (newpage > 0x999)
+        newpage = 0x999;
+    return newpage;
+}
+
+/**
+ * \brief toggles teletext page displaying mode
+ * \param priv_vbi private data structure
+ * \param flag new mode
+ * \return 
+ *   TVI_CONTROL_TRUE is success,
+ *   TVI_CONTROL_FALSE otherwise
+ *
+ * flag:
+ * 0 - off
+ * 1 - on & opaque
+ * 2 - on & transparent
+ * 3 - on & transparent  with black foreground color (only in bw mode)
+ *
+ */
+static int teletext_set_mode(priv_vbi_t * priv_vbi, int flag)
+{
+    if (flag<0 || flag>3)
+        return TVI_CONTROL_FALSE;
+        
+    pthread_mutex_lock(&(priv_vbi->buffer_mutex));
+
+    priv_vbi->on = flag;
+
+    if (priv_vbi->on > 2 && priv_vbi->tformat != VBI_TFORMAT_BW)
+        priv_vbi->on = 0;
+
+    priv_vbi->foreground = 0;
+    priv_vbi->pagenumdec = 0;
+    priv_vbi->spudec_proc = 1;
+    priv_vbi->redraw = 1;
+    switch (priv_vbi->on) {
+    case 0:
+        priv_vbi->csize = 0;
+        break;
+    case 1:
+        priv_vbi->alpha = 0;
+        break;
+    case 2:
+        priv_vbi->alpha = 1;
+        break;
+    case 3:
+        priv_vbi->alpha = 1;
+        priv_vbi->foreground = 1;
+        break;
+    }
+    pthread_mutex_unlock(&(priv_vbi->buffer_mutex));
+    return TVI_CONTROL_TRUE;
+}
+
+/**
+ * \brief get half page mode (only in SPU mode)
+ * \param priv_vbi private data structure
+ * \return current mode
+ *     0 : half mode off
+ *     1 : top half page
+ *     2 : bottom half page
+ */
+static int vbi_get_half(priv_vbi_t * priv_vbi)
+{
+    int flag = 0;
+    pthread_mutex_lock(&(priv_vbi->buffer_mutex));
+    if (priv_vbi->valid_page)
+        flag = priv_vbi->half;
+    priv_vbi->pagenumdec = 0;
+    pthread_mutex_unlock(&(priv_vbi->buffer_mutex));
+    return flag;
+}
+
+/**
+ * \brief set half page mode (only in SPU mode)
+ * \param priv_vbi private data structure
+ * \param flag new half page mode
+ * \return 
+ *   TVI_CONTROL_TRUE is success,
+ *   TVI_CONTROL_FALSE otherwise
+ *
+ *
+ *  flag:
+ *     0 : half mode off
+ *     1 : top half page
+ *     2 : bottom half page
+ */
+static int teletext_set_half_page(priv_vbi_t * priv_vbi, int flag)
+{
+    if (flag<0 || flag>2)
+        return TVI_CONTROL_FALSE;
+
+    pthread_mutex_lock(&(priv_vbi->buffer_mutex));
+    priv_vbi->half = flag;
+    if (priv_vbi->tformat == VBI_TFORMAT_TEXT && priv_vbi->half > 1)
+        priv_vbi->half = 0;
+    priv_vbi->redraw = 1;
+    priv_vbi->pagenumdec = 0;
+    pthread_mutex_unlock(&(priv_vbi->buffer_mutex));
+    return TVI_CONTROL_TRUE;
+}
+
+/**
+ * \brief displays specified page
+ * \param priv_vbi private data structure
+ * \param pgno page number to display
+ * \param subno subpage number
+ *
+ */
+static 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->redraw = 1;
+    priv_vbi->pagenumdec = 0;
+    pthread_mutex_unlock(&(priv_vbi->buffer_mutex));
+}
+
+/**
+ * \brief steps over pages by a given value
+ * \param priv_vbi private data structure
+ * \param direction decimal step value (can be negative)
+ *
+ */
+static 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->redraw = 1;
+    priv_vbi->pagenumdec = 0;
+    pthread_mutex_unlock(&(priv_vbi->buffer_mutex));
+}
+
+/**
+ * \brief append just entered digit to editing page number
+ * \param priv_vbi private data structure
+ * \param dec decimal digit to append
+ *
+ *  dec: 
+ *   '0'..'9' append digit
+ *    '-' remove last digit (backspace emulation)
+ *
+ * This routine allows user to jump to arbitrary page.
+ * It implements simple page number editing algorithm.
+ *
+ * Subsystem can be on one of two modes: normal and page number edit mode.
+ * Zero value of priv_vbi->pagenumdec means normal mode
+ * Non-zero value means page number edit mode and equals to packed
+ * decimal number of already entered part of page number.
+ *
+ * How this works.
+ * Let's assume that current mode is normal (pagenumdec is zero), teletext page 
+ * 100 are displayed as usual. topmost left corner of page contains page number.
+ * Then vbi_add_dec is sequentally called (through slave 
+ * command of course) with 1,4,-,2,3 * values of dec parameter.
+ *
+ * +-----+------------+------------------+
+ * | dec | pagenumxec | displayed number |
+ * +-----+------------+------------------+
+ * |     | 0x000      | 100              | 
+ * +-----+------------+------------------+
+ * | 1   | 0x001      | __1              |
+ * +-----+------------+------------------+
+ * | 4   | 0x014      | _14              |
+ * +-----+------------+------------------+
+ * | -   | 0x001      | __1              |
+ * +-----+------------+------------------+
+ * | 2   | 0x012      | _12              |
+ * +-----+------------+------------------+
+ * | 3   | 0x123      | 123              |
+ * +-----+------------+------------------+
+ * |     | 0x000      | 123              | 
+ * +-----+------------+------------------+
+ *
+ * pagenumdec will automatically receive zero value after third digit of page number
+ * is entered and current page will be switched to another one with entered page number.
+ *
+ */
+static void vbi_add_dec(priv_vbi_t * priv_vbi, char *dec)
+{
+    int count, shift;
+    if (!dec)
+        return;
+    if (!priv_vbi->on)
+        return;
+    if ((*dec < '0' || *dec > '9') && *dec != '-')
+        return;
+    pthread_mutex_lock(&(priv_vbi->buffer_mutex));
+    count = (priv_vbi->pagenumdec >> 12) & 0xf;
+    if (*dec == '-') {
+        count--;
+        if (count)
+            priv_vbi->pagenumdec = ((priv_vbi->pagenumdec >> 4) & 0xfff) | (count << 12);
+        else
+            priv_vbi->pagenumdec = 0;
+    } else {
+        shift = count * 4;
+        count++;
+        priv_vbi->pagenumdec =
+            (((priv_vbi->pagenumdec) << 4 | (*dec -'0')) & 0xfff) | (count << 12);
+        if (count == 3) {
+            priv_vbi->pgno = priv_vbi->pagenumdec & 0xfff;
+            priv_vbi->subno = 0;
+            priv_vbi->redraw = 1;
+            priv_vbi->pagenumdec = 0;
+        }
+    }
+    pthread_mutex_unlock(&(priv_vbi->buffer_mutex));
+}
+
+/**
+ * \brief follows link specified on current page
+ * \param priv_vbi private data structure
+ * \param linkno link number (0..6)
+ * \return 
+ *    TVI_CONTROL_FALSE if linkno is outside 0..6 range or if
+ *                      teletext is switched off
+ *    TVI_CONTROL_TRUE otherwise
+ *
+ * linkno: 
+ *    0: tpage in tv parameters (starting page, usually 100)
+ * 1..6: follows link on current page with given number
+ *
+ * FIXME: quick test shows that this is working strange
+ * FIXME: routine does not checks whether links exists on page or not
+ * TODO: more precise look
+ *
+ */
+static int vbi_golink(priv_vbi_t * priv_vbi, int linkno)
+{
+    if (linkno < 0 || linkno > 6)
+        return TVI_CONTROL_FALSE;
+    if (!priv_vbi->on)
+        return TVI_CONTROL_FALSE;
+    pthread_mutex_lock(&(priv_vbi->buffer_mutex));
+    if (linkno == 0) {
+        priv_vbi->pgno = priv_vbi->tpage;
+        priv_vbi->subno = priv_vbi->page->nav_link[linkno].subno;
+        priv_vbi->redraw = 1;
+        priv_vbi->pagenumdec = 0;
+    } else {
+        linkno--;
+        if (priv_vbi->pgno == priv_vbi->page->pgno) {
+            priv_vbi->pgno = priv_vbi->page->nav_link[linkno].pgno;
+            priv_vbi->subno = priv_vbi->page->nav_link[linkno].subno;
+            priv_vbi->redraw = 1;
+            priv_vbi->pagenumdec = 0;
+        }
+    }
+    priv_vbi->pagenumdec = 0;
+    pthread_mutex_unlock(&(priv_vbi->buffer_mutex));
+    return TVI_CONTROL_TRUE;
+}
+
+/**
+ * \brief get pointer to current teletext page
+ * \param priv_vbi private data structure
+ * \return pointer to vbi_page structure if teletext is
+ *         switched on and current page is valid, NULL - otherwise
+ *    
+ */
+static vbi_page *vbi_getpage(priv_vbi_t * priv_vbi)
+{
+    vbi_page *page = NULL;
+
+    if (!priv_vbi->on)
+        return NULL;
+    pthread_mutex_lock(&(priv_vbi->buffer_mutex));
+    if (priv_vbi->valid_page)
+        if (page = malloc(sizeof(vbi_page)))
+            memcpy(page, priv_vbi->page, sizeof(vbi_page));
+    pthread_mutex_unlock(&(priv_vbi->buffer_mutex));
+    return page;
+}
+
+/**
+ * \brief get pointer to current teletext page
+ * \param priv_vbi private data structure
+ * \return pointer to character string, containing text-only data of
+ *         teletext page. If teletext is switched off, current page is invalid
+ *         or page format if not equal to "text" then returning value is NULL.
+ * 
+ */
+static char *vbi_getpagetext(priv_vbi_t * priv_vbi)
+{
+    char *page = NULL;
+
+    if (!priv_vbi->on)
+        return NULL;
+    if (priv_vbi->tformat != VBI_TFORMAT_TEXT && priv_vbi->canvas)
+        return NULL;
+    pthread_mutex_lock(&(priv_vbi->buffer_mutex));
+    if (priv_vbi->valid_page)
+        page = priv_vbi->txtpage;
+    if (!page)
+        page = priv_vbi->header;
+    pthread_mutex_unlock(&(priv_vbi->buffer_mutex));
+    return page;
+}
+
+/**
+ * \brief get current page RGBA32 image (only in SPU mode)
+ * \param priv_vbi private data structure
+ * \return pointer to tv_teletext_img_t structure, containing among
+ *         other things rendered RGBA32 image of current teletext page.
+ *         return NULL is image is not available for some reason.
+ *
+ */
+static tv_teletext_img_t *vbi_getpageimg(priv_vbi_t * priv_vbi)
+{
+    tv_teletext_img_t *img = NULL;
+
+    if (priv_vbi->tformat == VBI_TFORMAT_TEXT)
+        return NULL;
+    if (priv_vbi->spudec_proc == 0)
+        return 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->tformat = VBI_TFORMAT_GRAY;         // format: bw|gray|color
+        img->half = priv_vbi->half;              // half mode
+        img->columns = priv_vbi->columns;        // page size
+        img->rows = priv_vbi->rows;
+        img->width = priv_vbi->columns * 12;
+        img->width = priv_vbi->rows * 10;
+        img->canvas = NULL;
+        // is page ok?
+        if (priv_vbi->canvas && priv_vbi->on && priv_vbi->csize && priv_vbi->valid_page) { 
+            
+            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;
+}
+
+/**
+ * \brief start teletext sybsystem
+ * \param priv_vbi private data structure
+ *
+ * initializes cache, vbi decoder and starts background thread
+ *
+ */
+static void vbi_start(priv_vbi_t * priv_vbi)
+{
+    if (!priv_vbi)
+        return;
+    if (NULL != (priv_vbi->txtpage = malloc(VBI_TXT_PAGE_SIZE)))        // alloc vbi_page
+        memset(priv_vbi->txtpage, 0, VBI_TXT_PAGE_SIZE);
+    priv_vbi->page = malloc(sizeof(vbi_page));
+    priv_vbi->cache = (vbi_page **) malloc(1000 * sizeof(vbi_page *));
+    memset(priv_vbi->cache, 0, 1000 * sizeof(vbi_page *));
+    priv_vbi->decoder = vbi_decoder_new();
+    priv_vbi->subno = 0;
+    priv_vbi->fmt = VBI_PIXFMT_RGBA32_LE;
+    memset(priv_vbi->theader, 0, sizeof(priv_vbi->theader));
+    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->valid_page = 0;
+    priv_vbi->pagenumdec = 0;
+    mp_msg(MSGT_TV, MSGL_INFO, "Teletext device: %s\n", priv_vbi->device);
+}
+
+/**
+ * \brief Teletext reset
+ * \param priv_vbi private data structure
+ *
+ * should be called during frequency, norm change, etc
+ *
+ */
+static void vbi_reset(priv_vbi_t * priv_vbi)
+{
+    int i;
+    pthread_mutex_lock(&(priv_vbi->buffer_mutex));
+    if (priv_vbi->canvas)
+        free(priv_vbi->canvas);
+    priv_vbi->canvas = NULL;
+    priv_vbi->canvas_size = 0;
+    priv_vbi->redraw = 1;
+    priv_vbi->csize = 0;
+    priv_vbi->valid_page = 0;
+    priv_vbi->spudec_proc = 1;
+    priv_vbi->pagenumdec = 0;
+    if (priv_vbi->page)
+        memset(priv_vbi->page, 0, sizeof(vbi_page));
+    if (priv_vbi->txtpage)
+        memset(priv_vbi->txtpage, 0, VBI_TXT_PAGE_SIZE);
+    memset(priv_vbi->theader, 0, sizeof(priv_vbi->theader));
+    if (priv_vbi->cache) {
+        for (i = 0; i < 1000; i++) {
+            if (priv_vbi->cache[i])
+                free(priv_vbi->cache[i]);
+            priv_vbi->cache[i] = NULL;
+        }
+    }
+    snprintf(priv_vbi->header, sizeof(priv_vbi->header), "%s",
+             VBI_NO_TELETEXT);
+    pthread_mutex_unlock(&(priv_vbi->buffer_mutex));
+}
+
+/*
+---------------------------------------------------------------------------------
+    Public routines
+---------------------------------------------------------------------------------
+*/
+
+/**
+ * \brief teletext subsystem init
+ * \note  Routine uses global variables tv_param_tdevice, tv_param_tpage
+ *        and tv_param_tformat for initialization.
+ *
+ */
+priv_vbi_t *teletext_init(void)
+{
+    priv_vbi_t *priv_vbi;
+    int formatid, startpage;
+    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;
+
+    if (!tv_param_tdevice)
+        return NULL;
+
+    if (NULL == (priv_vbi = malloc(sizeof(priv_vbi_t))))
+        return NULL;
+    memset(priv_vbi, 0, sizeof(priv_vbi_t));
+    formatid = VBI_TFORMAT_TEXT;         // default
+    if (tv_param_tformat != NULL) {
+        if (strcmp(tv_param_tformat, "text") == 0)
+            formatid = VBI_TFORMAT_TEXT;
+        if (strcmp(tv_param_tformat, "bw") == 0)
+            formatid = VBI_TFORMAT_BW;
+        if (strcmp(tv_param_tformat, "gray") == 0)
+            formatid = VBI_TFORMAT_GRAY;
+        if (strcmp(tv_param_tformat, "color") == 0)
+            formatid = VBI_TFORMAT_COLOR;
+    }
+    startpage = steppage(0, tv_param_tpage);         // page number is HEX
+    if (startpage < 0x100 || startpage > 0x999)
+        startpage = 0x100;
+    priv_vbi->device = strdup(tv_param_tdevice);
+    priv_vbi->tformat = formatid;
+    priv_vbi->tpage = startpage;  // page number
+    priv_vbi->pgno = startpage;          // page number
+
+
+    if (!priv_vbi->capture) {
+        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) {
+        free(priv_vbi->device);
+        free(priv_vbi);
+        mp_msg(MSGT_TV, MSGL_INFO, "No teletext\n");
+        return NULL;
+    }
+    return priv_vbi;
+}
+
+/**
+ * \brief teletext subsystem uninitialization
+ * \param priv_vbi private data structure
+ *
+ * closes vbi capture, decode and and frees priv_vbi structure
+ *
+ */
+void teletext_uninit(priv_vbi_t * priv_vbi)
+{
+    int i;
+    if (priv_vbi == NULL)
+        return;
+    priv_vbi->eof = 1;
+    if (priv_vbi->capture){
+        vbi_capture_delete(priv_vbi->capture);
+        priv_vbi->capture = NULL;
+    }
+    if (priv_vbi->decoder){
+        vbi_event_handler_remove(priv_vbi->decoder, event_handler);
+        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->txtpage){
+        free(priv_vbi->txtpage);
+        priv_vbi->txtpage = NULL;
+    }
+    if (priv_vbi->network_name){
+        free(priv_vbi->network_name);
+        priv_vbi->network_name = NULL;
+    }
+    if (priv_vbi->network_id){
+        free(priv_vbi->network_id);
+        priv_vbi->network_id = NULL;
+    }
+    if (priv_vbi->page){
+        free(priv_vbi->page);
+        priv_vbi->page = NULL;
+    }
+    if (priv_vbi->cache) {
+        for (i = 0; i < 1000; i++) {
+            if (priv_vbi->cache[i])
+                free(priv_vbi->cache[i]);
+        }
+        free(priv_vbi->cache);
+        priv_vbi->cache = NULL;
+    }
+    free(priv_vbi);
+}
+
+/**
+ * \brief Teletext control routine
+ * \param priv_vbi private data structure
+ * \param cmd command 
+ * \param arg command parameter (has to be not null)
+ *
+ */
+int teletext_control(priv_vbi_t * priv_vbi, int cmd, void *arg)
+{
+    vbi_page *page = NULL;
+    char *txtpage = NULL;
+    tv_teletext_img_t *img = NULL;
+    if (!priv_vbi)
+        return TVI_CONTROL_FALSE;
+    if (!arg)
+        return TVI_CONTROL_FALSE;
+    switch (cmd) {
+    case TVI_CONTROL_VBI_RESET:
+        vbi_reset(priv_vbi);
+        return TVI_CONTROL_TRUE;
+    case TVI_CONTROL_VBI_START:
+        vbi_start(priv_vbi);
+        return TVI_CONTROL_TRUE;
+    case TVI_CONTROL_VBI_GET_FORMAT:
+        pthread_mutex_lock(&(priv_vbi->buffer_mutex));
+        *(int*)arg=priv_vbi->tformat;
+        pthread_mutex_unlock(&(priv_vbi->buffer_mutex));
+	return TVI_CONTROL_TRUE;
+    case TVI_CONTROL_VBI_SET_MODE:
+        return teletext_set_mode(priv_vbi, *(int *) arg);
+    case TVI_CONTROL_VBI_GET_MODE:
+        pthread_mutex_lock(&(priv_vbi->buffer_mutex));
+        *(int*)arg=priv_vbi->on;
+        pthread_mutex_unlock(&(priv_vbi->buffer_mutex));
+	return TVI_CONTROL_TRUE;
+    case TVI_CONTROL_VBI_STEP_MODE:
+    {
+        int val;
+        pthread_mutex_lock(&(priv_vbi->buffer_mutex));
+	val=(priv_vbi->on+*(int*)arg)%4;
+        pthread_mutex_unlock(&(priv_vbi->buffer_mutex));
+	if (val<0)
+	    val+=4;
+	return teletext_set_mode(priv_vbi,val);
+    }
+    case TVI_CONTROL_VBI_GET_HALF_PAGE:
+        *(void **) arg = (void *) vbi_get_half(priv_vbi);
+        return TVI_CONTROL_TRUE;
+    case TVI_CONTROL_VBI_SET_HALF_PAGE:
+        return teletext_set_half_page(priv_vbi, *(int *) arg);
+    case TVI_CONTROL_VBI_STEP_HALF_PAGE:
+    {
+        int val;
+	val=(vbi_get_half(priv_vbi)+*(int*)arg)%3;
+	
+	if (val<0)
+	    val+=3;
+	return teletext_set_half_page(priv_vbi,val);
+    }
+
+    case TVI_CONTROL_VBI_SET_PAGE:
+        vbi_setpage(priv_vbi, *(int *) arg, 0);
+        return TVI_CONTROL_TRUE;
+    case TVI_CONTROL_VBI_STEP_PAGE:
+        vbi_steppage(priv_vbi, *(int *) arg);
+        return TVI_CONTROL_TRUE;
+    case TVI_CONTROL_VBI_ADD_DEC:
+        vbi_add_dec(priv_vbi, *(char **) arg);
+        return TVI_CONTROL_TRUE;
+    case TVI_CONTROL_VBI_GO_LINK:
+        return vbi_golink(priv_vbi, *(int *) arg);
+    case TVI_CONTROL_VBI_GET_PAGE:
+        *(int*) arg = priv_vbi->pgno;
+        return TVI_CONTROL_TRUE;
+    case TVI_CONTROL_VBI_GET_VBIPAGE:
+        if (NULL == (page = vbi_getpage(priv_vbi)))
+            return TVI_CONTROL_FALSE;
+        *(void **) arg = (void *) page;
+        return TVI_CONTROL_TRUE;
+    case TVI_CONTROL_VBI_GET_TXTPAGE:
+        if (NULL == (txtpage = vbi_getpagetext(priv_vbi)))
+            return TVI_CONTROL_FALSE;
+        *(void **) arg = (void *) txtpage;
+        return TVI_CONTROL_TRUE;
+    case TVI_CONTROL_VBI_GET_IMGPAGE:
+        if (NULL == (img = vbi_getpageimg(priv_vbi)))
+            return TVI_CONTROL_FALSE;
+        *(void **) arg = (void *) img;
+        return TVI_CONTROL_TRUE;
+    }
+    return TVI_CONTROL_UNKNOWN;
+}

Added: trunk/stream/tvi_vbi.h
==============================================================================
--- (empty file)
+++ trunk/stream/tvi_vbi.h	Sun Jun 10 02:06:12 2007
@@ -0,0 +1,75 @@
+#ifndef __TVI_VBI_H_
+#define __TVI_VBI_H_
+
+#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"
+
+#define VBI_TRANSPARENT_COLOR    40         ///< transparent color id
+#define VBI_TIME_LINEPOS    13              ///< time line pos in page header
+
+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;
+    pthread_mutex_t    update_mutex;
+    int            eof;                     ///< end grab
+    int           tpage;                    ///< tpage
+    int            pgno;                    ///< seek page number
+    int            subno;                   ///< seek subpage
+    int            curr_pgno;               ///< current page number
+    int            curr_subno;              ///< current subpage
+    uint32_t       pagenumdec;              ///< set page num with dec
+
+    vbi_page** cache;
+    vbi_page         *page;                 ///< vbi_page
+    int            valid_page;              ///< valid page flag
+    char*        txtpage;                   ///< decoded vbi_page to text
+    vbi_char    theader[VBI_MAX_LINE_SIZE]; ///< vbi header
+    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            foreground;              ///< foreground black in bw 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;
+
+/// teletext subsystem initialization
+priv_vbi_t* teletext_init(void);
+/// teletext subsystem uninitialization
+void teletext_uninit(priv_vbi_t* priv_vbi);
+/// ioctl for 
+int teletext_control(priv_vbi_t* priv_vbi, int cmd, void *args);
+#endif



More information about the MPlayer-cvslog mailing list