diff options
Diffstat (limited to 'stream/tvi_vbi.c')
-rw-r--r-- | stream/tvi_vbi.c | 1197 |
1 files changed, 1197 insertions, 0 deletions
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) + * 1..6: follows link on current page with given number + * + * FIXME: quick test shows that this is working strange + * FIXME: routine does not checks whether links exists on page or not + * TODO: more precise look + * + */ +static int vbi_golink(priv_vbi_t * priv_vbi, int linkno) +{ + if (linkno < 0 || linkno > 6) + return TVI_CONTROL_FALSE; + if (!priv_vbi->on) + return TVI_CONTROL_FALSE; + pthread_mutex_lock(&(priv_vbi->buffer_mutex)); + if (linkno == 0) { + priv_vbi->pgno = priv_vbi->tpage; + priv_vbi->subno = priv_vbi->page->nav_link[linkno].subno; + priv_vbi->redraw = 1; + priv_vbi->pagenumdec = 0; + } else { + linkno--; + if (priv_vbi->pgno == priv_vbi->page->pgno) { + priv_vbi->pgno = priv_vbi->page->nav_link[linkno].pgno; + priv_vbi->subno = priv_vbi->page->nav_link[linkno].subno; + priv_vbi->redraw = 1; + priv_vbi->pagenumdec = 0; + } + } + priv_vbi->pagenumdec = 0; + pthread_mutex_unlock(&(priv_vbi->buffer_mutex)); + return TVI_CONTROL_TRUE; +} + +/** + * \brief get pointer to current teletext page + * \param priv_vbi private data structure + * \return pointer to vbi_page structure if teletext is + * switched on and current page is valid, NULL - otherwise + * + */ +static vbi_page *vbi_getpage(priv_vbi_t * priv_vbi) +{ + vbi_page *page = NULL; + + if (!priv_vbi->on) + return NULL; + pthread_mutex_lock(&(priv_vbi->buffer_mutex)); + if (priv_vbi->valid_page) + if (page = malloc(sizeof(vbi_page))) + memcpy(page, priv_vbi->page, sizeof(vbi_page)); + pthread_mutex_unlock(&(priv_vbi->buffer_mutex)); + return page; +} + +/** + * \brief get pointer to current teletext page + * \param priv_vbi private data structure + * \return pointer to character string, containing text-only data of + * teletext page. If teletext is switched off, current page is invalid + * or page format if not equal to "text" then returning value is NULL. + * + */ +static char *vbi_getpagetext(priv_vbi_t * priv_vbi) +{ + char *page = NULL; + + if (!priv_vbi->on) + return NULL; + if (priv_vbi->tformat != VBI_TFORMAT_TEXT && priv_vbi->canvas) + return NULL; + pthread_mutex_lock(&(priv_vbi->buffer_mutex)); + if (priv_vbi->valid_page) + page = priv_vbi->txtpage; + if (!page) + page = priv_vbi->header; + pthread_mutex_unlock(&(priv_vbi->buffer_mutex)); + return page; +} + +/** + * \brief get current page RGBA32 image (only in SPU mode) + * \param priv_vbi private data structure + * \return pointer to tv_teletext_img_t structure, containing among + * other things rendered RGBA32 image of current teletext page. + * return NULL is image is not available for some reason. + * + */ +static tv_teletext_img_t *vbi_getpageimg(priv_vbi_t * priv_vbi) +{ + tv_teletext_img_t *img = NULL; + + if (priv_vbi->tformat == VBI_TFORMAT_TEXT) + return NULL; + if (priv_vbi->spudec_proc == 0) + return NULL; + pthread_mutex_lock(&(priv_vbi->buffer_mutex)); + if (NULL != (img = malloc(sizeof(tv_teletext_img_t)))) { + img->tformat = priv_vbi->tformat; // format: bw|gray|color + img->tformat = VBI_TFORMAT_GRAY; // format: bw|gray|color + img->half = priv_vbi->half; // half mode + img->columns = priv_vbi->columns; // page size + img->rows = priv_vbi->rows; + img->width = priv_vbi->columns * 12; + img->width = priv_vbi->rows * 10; + img->canvas = NULL; + // is page ok? + if (priv_vbi->canvas && priv_vbi->on && priv_vbi->csize && priv_vbi->valid_page) { + + if (NULL != (img->canvas = malloc(priv_vbi->csize))) + memcpy(img->canvas, priv_vbi->canvas, priv_vbi->csize); + } + } + priv_vbi->spudec_proc = 0; + pthread_mutex_unlock(&(priv_vbi->buffer_mutex)); + return img; +} + +/** + * \brief start teletext sybsystem + * \param priv_vbi private data structure + * + * initializes cache, vbi decoder and starts background thread + * + */ +static void vbi_start(priv_vbi_t * priv_vbi) +{ + if (!priv_vbi) + return; + if (NULL != (priv_vbi->txtpage = malloc(VBI_TXT_PAGE_SIZE))) // alloc vbi_page + memset(priv_vbi->txtpage, 0, VBI_TXT_PAGE_SIZE); + priv_vbi->page = malloc(sizeof(vbi_page)); + priv_vbi->cache = (vbi_page **) malloc(1000 * sizeof(vbi_page *)); + memset(priv_vbi->cache, 0, 1000 * sizeof(vbi_page *)); + priv_vbi->decoder = vbi_decoder_new(); + priv_vbi->subno = 0; + priv_vbi->fmt = VBI_PIXFMT_RGBA32_LE; + memset(priv_vbi->theader, 0, sizeof(priv_vbi->theader)); + snprintf(priv_vbi->header, sizeof(priv_vbi->header), "%s", VBI_NO_TELETEXT); + vbi_event_handler_add(priv_vbi->decoder, ~0, event_handler, (void *) priv_vbi); // add event handler + pthread_create(&priv_vbi->grabber_thread, NULL, grabber, priv_vbi); // add grab function + pthread_mutex_init(&priv_vbi->buffer_mutex, NULL); + priv_vbi->valid_page = 0; + priv_vbi->pagenumdec = 0; + mp_msg(MSGT_TV, MSGL_INFO, "Teletext device: %s\n", priv_vbi->device); +} + +/** + * \brief Teletext reset + * \param priv_vbi private data structure + * + * should be called during frequency, norm change, etc + * + */ +static void vbi_reset(priv_vbi_t * priv_vbi) +{ + int i; + pthread_mutex_lock(&(priv_vbi->buffer_mutex)); + if (priv_vbi->canvas) + free(priv_vbi->canvas); + priv_vbi->canvas = NULL; + priv_vbi->canvas_size = 0; + priv_vbi->redraw = 1; + priv_vbi->csize = 0; + priv_vbi->valid_page = 0; + priv_vbi->spudec_proc = 1; + priv_vbi->pagenumdec = 0; + if (priv_vbi->page) + memset(priv_vbi->page, 0, sizeof(vbi_page)); + if (priv_vbi->txtpage) + memset(priv_vbi->txtpage, 0, VBI_TXT_PAGE_SIZE); + memset(priv_vbi->theader, 0, sizeof(priv_vbi->theader)); + if (priv_vbi->cache) { + for (i = 0; i < 1000; i++) { + if (priv_vbi->cache[i]) + free(priv_vbi->cache[i]); + priv_vbi->cache[i] = NULL; + } + } + snprintf(priv_vbi->header, sizeof(priv_vbi->header), "%s", + VBI_NO_TELETEXT); + pthread_mutex_unlock(&(priv_vbi->buffer_mutex)); +} + +/* +--------------------------------------------------------------------------------- + Public routines +--------------------------------------------------------------------------------- +*/ + +/** + * \brief teletext subsystem init + * \note Routine uses global variables tv_param_tdevice, tv_param_tpage + * and tv_param_tformat for initialization. + * + */ +priv_vbi_t *teletext_init(void) +{ + priv_vbi_t *priv_vbi; + int formatid, startpage; + unsigned int services = VBI_SLICED_TELETEXT_B | + VBI_SLICED_CAPTION_525 | + VBI_SLICED_CAPTION_625 | + VBI_SLICED_VBI_525 | + VBI_SLICED_VBI_625 | + VBI_SLICED_WSS_625 | + VBI_SLICED_WSS_CPR1204 | + VBI_SLICED_VPS; + + if (!tv_param_tdevice) + return NULL; + + if (NULL == (priv_vbi = malloc(sizeof(priv_vbi_t)))) + return NULL; + memset(priv_vbi, 0, sizeof(priv_vbi_t)); + formatid = VBI_TFORMAT_TEXT; // default + if (tv_param_tformat != NULL) { + if (strcmp(tv_param_tformat, "text") == 0) + formatid = VBI_TFORMAT_TEXT; + if (strcmp(tv_param_tformat, "bw") == 0) + formatid = VBI_TFORMAT_BW; + if (strcmp(tv_param_tformat, "gray") == 0) + formatid = VBI_TFORMAT_GRAY; + if (strcmp(tv_param_tformat, "color") == 0) + formatid = VBI_TFORMAT_COLOR; + } + startpage = steppage(0, tv_param_tpage); // page number is HEX + if (startpage < 0x100 || startpage > 0x999) + startpage = 0x100; + priv_vbi->device = strdup(tv_param_tdevice); + priv_vbi->tformat = formatid; + priv_vbi->tpage = startpage; // page number + priv_vbi->pgno = startpage; // page number + + + if (!priv_vbi->capture) { + priv_vbi->services = services; // probe v4l2 + priv_vbi->capture = vbi_capture_v4l2_new(priv_vbi->device, // device + 20, // buffer numbers + &(priv_vbi->services), // services + 0, // strict + &(priv_vbi->errstr), // error string + 0); // trace + } + services = priv_vbi->services; |