diff options
Diffstat (limited to 'stream')
-rw-r--r-- | stream/Makefile | 1 | ||||
-rw-r--r-- | stream/tv.c | 75 | ||||
-rw-r--r-- | stream/tv.h | 59 | ||||
-rw-r--r-- | stream/tvi_vbi.c | 1197 | ||||
-rw-r--r-- | stream/tvi_vbi.h | 75 |
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) + 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) |