summaryrefslogtreecommitdiffstats
path: root/stream
diff options
context:
space:
mode:
authorvoroshil <voroshil@b3059339-0415-0410-9bf9-f77b7e298cf2>2007-06-10 00:06:12 +0000
committervoroshil <voroshil@b3059339-0415-0410-9bf9-f77b7e298cf2>2007-06-10 00:06:12 +0000
commit83a2c50ef9ef648b576bf4299765c73212d1f480 (patch)
treeab09f836c3ca91641c96b0630b2b2f479a3a6c2f /stream
parent990b33b1c28d6b604a85e588689ad7fb6de59354 (diff)
downloadmpv-83a2c50ef9ef648b576bf4299765c73212d1f480.tar.bz2
mpv-83a2c50ef9ef648b576bf4299765c73212d1f480.tar.xz
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
Diffstat (limited to 'stream')
-rw-r--r--stream/Makefile1
-rw-r--r--stream/tv.c75
-rw-r--r--stream/tv.h59
-rw-r--r--stream/tvi_vbi.c1197
-rw-r--r--stream/tvi_vbi.h75
5 files changed, 1407 insertions, 0 deletions
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 <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@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)