From 83a2c50ef9ef648b576bf4299765c73212d1f480 Mon Sep 17 00:00:00 2001 From: voroshil Date: Sun, 10 Jun 2007 00:06:12 +0000 Subject: Teletext support for tv:// (v4l and v4l2 only) modified patch from Otvos Attila oattila at chello dot hu Module uses zvbi library for all low-level VBI operations (like I/O with vbi device, converting vbi pages into usefull vbi_page stuctures, rendering them into RGB32 images). All teletext related stuff (except properties, slave commands and rendering osd in text mode or RGB32 rendered teletext pages in spu mode) is implemented in tvi_vbi.c New properties: teletext_page - switching between pages teletext_mode - switch between on/off/opaque/transparent modes teletext_format - (currently read-only) allows to get format info (black/white,gray,text) teletext_half_page - trivial zooming (displaying top/bottom half of teletext page) New slave commands: teletext_add_dec - user interface for jumping to any page by editing page number interactively teletext_go_link - goes though links, specified on current page git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@23530 b3059339-0415-0410-9bf9-f77b7e298cf2 --- Changelog | 1 + DOCS/man/en/mplayer.1 | 26 ++ DOCS/tech/MAINTAINERS | 1 + DOCS/tech/slave.txt | 19 +- cfg-common.h | 5 + command.c | 149 ++++++ configure | 30 ++ input/input.c | 10 + input/input.h | 2 + libvo/sub.c | 130 ++++++ libvo/sub.h | 10 + mpcommon.c | 31 ++ mplayer.c | 13 +- spudec.c | 115 +++++ spudec.h | 3 + stream/Makefile | 1 + stream/tv.c | 75 ++++ stream/tv.h | 59 +++ stream/tvi_vbi.c | 1197 +++++++++++++++++++++++++++++++++++++++++++++++++ stream/tvi_vbi.h | 75 ++++ 20 files changed, 1950 insertions(+), 2 deletions(-) create mode 100644 stream/tvi_vbi.c create mode 100644 stream/tvi_vbi.h diff --git a/Changelog b/Changelog index be796a0245..db40f27ac4 100644 --- a/Changelog +++ b/Changelog @@ -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 diff --git a/DOCS/man/en/mplayer.1 b/DOCS/man/en/mplayer.1 index 2fb65fedc8..d60c50837a 100644 --- a/DOCS/man/en/mplayer.1 +++ b/DOCS/man/en/mplayer.1 @@ -421,6 +421,26 @@ Confirm choice. .PP .RS . +(The following keys are only valid if teletext support is enabled during +compilation: they are used for controlling TV teletext) +.RE +.PP +.PD 0 +.RS +.IPs "X" +Switch teletext between on, off and transparent mode. +.IPs "Q" +Next teletext page. +.IPs "W" +Previous teletext page. +.IPs "E" +In text mode flips top or bottom piece of page otherwise +zooms teletext page: top or bottom or normal. +.RE +.PD 1 +.PP +.RS +. .TP .B mouse control .PD 0 @@ -1851,6 +1871,12 @@ MJPEG compression: .IPs quality=<0\-100> Choose the quality of the JPEG compression (< 60 recommended for full size). +.IPs tdevice= (default: none) +Specify TV teletext device (example: /dev/\:vbi0). +.IPs tformat= (default: gray) +Specify TV teletext display mode. (Note: color mode requires color SPU support.) +.IPs tpage=<100-999> (default: 100) +Specify starting TV teletext page number . .RE . .TP diff --git a/DOCS/tech/MAINTAINERS b/DOCS/tech/MAINTAINERS index bddb74f2e8..a0b3c4832a 100644 --- a/DOCS/tech/MAINTAINERS +++ b/DOCS/tech/MAINTAINERS @@ -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 diff --git a/DOCS/tech/slave.txt b/DOCS/tech/slave.txt index 09e1ef31ea..b1d1158ebb 100644 --- a/DOCS/tech/slave.txt +++ b/DOCS/tech/slave.txt @@ -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 + 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 + 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 Select next/previous TV channel. @@ -446,4 +459,8 @@ tv_brightness int -100 100 X X X 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 diff --git a/cfg-common.h b/cfg-common.h index 234b6ab9e2..2f32235b49 100644 --- a/cfg-common.h +++ b/cfg-common.h @@ -459,6 +459,11 @@ m_option_t tvopts_conf[]={ {"alsa", &tv_param_alsa, CONF_TYPE_FLAG, 0, 0, 1, NULL}, #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} diff --git a/command.c b/command.c index ca7338004d..5466176741 100644 --- a/command.c +++ b/command.c @@ -1415,6 +1415,134 @@ static int mp_property_tv_color(m_option_t * prop, int action, void *arg, #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_cmd_t * cmd) 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: diff --git a/configure b/configure index 00bc302f08..5b54801889 100755 --- a/configure +++ b/configure @@ -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 < +#include +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 diff --git a/input/input.c b/input/input.c index 0697ec8719..7086782198 100644 --- a/input/input.c +++ b/input/input.c @@ -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" }, diff --git a/input/input.h b/input/input.h index 8017221d7d..4fb51422b1 100644 --- a/input/input.h +++ b/input/input.h @@ -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 diff --git a/libvo/sub.c b/libvo/sub.c index 7a7069d12d..c901e28fe5 100644 --- a/libvo/sub.c +++ b/libvo/sub.c @@ -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_obj_t *obj, int dxs, int dys) { } #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 @@ -963,6 +1090,9 @@ void vo_draw_text(int dxs,int dys,void (*draw_alpha)(int x0,int y0, int w,int h, break; #ifdef USE_DVDNAV case OSDTYPE_DVDNAV: +#endif +#ifdef HAVE_TV_TELETEXT + case OSDTYPE_TELETEXT: #endif case OSDTYPE_OSD: case OSDTYPE_SUBTITLE: diff --git a/libvo/sub.h b/libvo/sub.h index 45d39bbd6b..efc0277ba4 100644 --- a/libvo/sub.h +++ b/libvo/sub.h @@ -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 diff --git a/mpcommon.c b/mpcommon.c index 9a5c52accc..7734c443d0 100644 --- a/mpcommon.c +++ b/mpcommon.c @@ -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_video, demux_stream_t *d_dvdsub, int reset) } 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 +} diff --git a/mplayer.c b/mplayer.c index e79f2586aa..693b7ed353 100644 --- a/mplayer.c +++ b/mplayer.c @@ -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_t *sh_video, demux_stream_t *d_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_frame) ++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 amount, int style) // 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(); } diff --git a/spudec.c b/spudec.c index 451269a2e3..aaa9128eb3 100644 --- a/spudec.c +++ b/spudec.c @@ -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_functions_t *hw_spu) 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;pyrows*h1;py++) { + iptr=spu->image+(py-hs)*spu->stride; // image ptr + aptr=spu->aimage+(py-hs)*spu->stride; // alpha ptr + for(px=0;pxcolumns*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 diff --git a/spudec.h b/spudec.h index 2e73ef5df8..d315be3d1b 100644 --- a/spudec.h +++ b/spudec.h @@ -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 diff --git a/stream/Makefile b/stream/Makefile index a5d7edcf37..1ea38018fa 100644 --- a/stream/Makefile +++ b/stream/Makefile @@ -51,6 +51,7 @@ SRCS_COMMON-$(TV) += stream_tv.c tv.c frequencies.c tvi_dummy.c 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 diff --git a/stream/tv.c b/stream/tv.c index dcb96f1126..4133d04939 100644 --- a/stream/tv.c +++ b/stream/tv.c @@ -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_t *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, unsigned long freq) 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* norm) 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", diff --git a/stream/tv.h b/stream/tv.h index 942060b37a..63e9afe78a 100644 --- a/stream/tv.h +++ b/stream/tv.h @@ -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, float step_interval); 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 diff --git a/stream/tvi_vbi.c b/stream/tvi_vbi.c new file mode 100644 index 0000000000..226cf9b342 --- /dev/null +++ b/stream/tvi_vbi.c @@ -0,0 +1,1197 @@ +#include "config.h" + +#include +#include +#include +#include + +#include "tv.h" +#include "tvi_vbi.h" +#include "mp_msg.h" +#include "libmpcodecs/img_format.h" + +#ifdef USE_ICONV +#include +#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 + * + * 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;icache[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 pri