#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 priv_vbi private data structure * \return pointer to tv_teletext_img_t structure, containing among * other things rendered RGBA32 image of current teletext page. * return NULL is image is not available for some reason. * */ static tv_teletext_img_t *vbi_getpageimg(priv_vbi_t * priv_vbi) { tv_teletext_img_t *img = NULL; if (priv_vbi->tformat == VBI_TFORMAT_TEXT) return NULL; if (priv_vbi->spudec_proc == 0) return NULL; pthread_mutex_lock(&(priv_vbi->buffer_mutex)); if (NULL != (img = malloc(sizeof(tv_teletext_img_t)))) { img->tformat = priv_vbi->tformat; // format: bw|gray|color img->tformat = VBI_TFORMAT_GRAY; // format: bw|gray|color img->half = priv_vbi->half; // half mode img->columns = priv_vbi->columns; // page size img->rows = priv_vbi->rows; img->width = priv_vbi->columns * 12; img->width = priv_vbi->rows * 10; img->canvas = NULL; // is page ok? if (priv_vbi->canvas && priv_vbi->on && priv_vbi->csize && priv_vbi->valid_page) { if (NULL != (img->canvas = malloc(priv_vbi->csize))) memcpy(img->canvas, priv_vbi->canvas, priv_vbi->csize); } } priv_vbi->spudec_proc = 0; pthread_mutex_unlock(&(priv_vbi->buffer_mutex)); return img; } /** * \brief start teletext sybsystem * \param priv_vbi private data structure * * initializes cache, vbi decoder and starts background thread * */ static void vbi_start(priv_vbi_t * priv_vbi) { if (!priv_vbi) return; if (NULL != (priv_vbi->txtpage = malloc(VBI_TXT_PAGE_SIZE))) // alloc vbi_page memset(priv_vbi->txtpage, 0, VBI_TXT_PAGE_SIZE); priv_vbi->page = malloc(sizeof(vbi_page)); priv_vbi->cache = (vbi_page **) malloc(1000 * sizeof(vbi_page *)); memset(priv_vbi->cache, 0, 1000 * sizeof(vbi_page *)); priv_vbi->decoder = vbi_decoder_new(); priv_vbi->subno = 0; priv_vbi->fmt = VBI_PIXFMT_RGBA32_LE; memset(priv_vbi->theader, 0, sizeof(priv_vbi->theader)); snprintf(priv_vbi->header, sizeof(priv_vbi->header), "%s", VBI_NO_TELETEXT); vbi_event_handler_add(priv_vbi->decoder, ~0, event_handler, (void *) priv_vbi); // add event handler pthread_create(&priv_vbi->grabber_thread, NULL, grabber, priv_vbi); // add grab function pthread_mutex_init(&priv_vbi->buffer_mutex, NULL); priv_vbi->valid_page = 0; priv_vbi->pagenumdec = 0; mp_msg(MSGT_TV, MSGL_INFO, "Teletext device: %s\n", priv_vbi->device); } /** * \brief Teletext reset * \param priv_vbi private data structure * * should be called during frequency, norm change, etc * */ static void vbi_reset(priv_vbi_t * priv_vbi) { int i; pthread_mutex_lock(&(priv_vbi->buffer_mutex)); if (priv_vbi->canvas) free(priv_vbi->canvas); priv_vbi->canvas = NULL; priv_vbi->canvas_size = 0; priv_vbi->redraw = 1; priv_vbi->csize = 0; priv_vbi->valid_page = 0; priv_vbi->spudec_proc = 1; priv_vbi->pagenumdec = 0; if (priv_vbi->page) memset(priv_vbi->page, 0, sizeof(vbi_page)); if (priv_vbi->txtpage) memset(priv_vbi->txtpage, 0, VBI_TXT_PAGE_SIZE); memset(priv_vbi->theader, 0, sizeof(priv_vbi->theader)); if (priv_vbi->cache) { for (i = 0; i < 1000; i++) { if (priv_vbi->cache[i]) free(priv_vbi->cache[i]); priv_vbi->cache[i] = NULL; } } snprintf(priv_vbi->header, sizeof(priv_vbi->header), "%s", VBI_NO_TELETEXT); pthread_mutex_unlock(&(priv_vbi->buffer_mutex)); } /* --------------------------------------------------------------------------------- Public routines --------------------------------------------------------------------------------- */ /** * \brief teletext subsystem init * \note Routine uses global variables tv_param_tdevice, tv_param_tpage * and tv_param_tformat for initialization. * */ priv_vbi_t *teletext_init(void) { priv_vbi_t *priv_vbi; int formatid, startpage; unsigned int services = VBI_SLICED_TELETEXT_B | VBI_SLICED_CAPTION_525 | VBI_SLICED_CAPTION_625 | VBI_SLICED_VBI_525 | VBI_SLICED_VBI_625 | VBI_SLICED_WSS_625 | VBI_SLICED_WSS_CPR1204 | VBI_SLICED_VPS; if (!tv_param_tdevice) return NULL; if (NULL == (priv_vbi = malloc(sizeof(priv_vbi_t)))) return NULL; memset(priv_vbi, 0, sizeof(priv_vbi_t)); formatid = VBI_TFORMAT_TEXT; // default if (tv_param_tformat != NULL) { if (strcmp(tv_param_tformat, "text") == 0) formatid = VBI_TFORMAT_TEXT; if (strcmp(tv_param_tformat, "bw") == 0) formatid = VBI_TFORMAT_BW; if (strcmp(tv_param_tformat, "gray") == 0) formatid = VBI_TFORMAT_GRAY; if (strcmp(tv_param_tformat, "color") == 0) formatid = VBI_TFORMAT_COLOR; } startpage = steppage(0, tv_param_tpage); // page number is HEX if (startpage < 0x100 || startpage > 0x999) startpage = 0x100; priv_vbi->device = strdup(tv_param_tdevice); priv_vbi->tformat = formatid; priv_vbi->tpage = startpage; // page number priv_vbi->pgno = startpage; // page number if (!priv_vbi->capture) { priv_vbi->services = services; // probe v4l2 priv_vbi->capture = vbi_capture_v4l2_new(priv_vbi->device, // device 20, // buffer numbers &(priv_vbi->services), // services 0, // strict &(priv_vbi->errstr), // error string 0); // trace } services = priv_vbi->services; if (priv_vbi->capture == NULL) { priv_vbi->services = services; // probe v4l priv_vbi->capture = vbi_capture_v4l_new(priv_vbi->device, 20, &(priv_vbi->services), 0, &(priv_vbi->errstr), 0); } if (!priv_vbi->capture) { free(priv_vbi->device); free(priv_vbi); mp_msg(MSGT_TV, MSGL_INFO, "No teletext\n"); return NULL; } return priv_vbi; } /** * \brief teletext subsystem uninitialization * \param priv_vbi private data structure * * closes vbi capture, decode and and frees priv_vbi structure * */ void teletext_uninit(priv_vbi_t * priv_vbi) { int i; if (priv_vbi == NULL) return; priv_vbi->eof = 1; if (priv_vbi->capture){ vbi_capture_delete(priv_vbi->capture); priv_vbi->capture = NULL; } if (priv_vbi->decoder){ vbi_event_handler_remove(priv_vbi->decoder, event_handler); vbi_decoder_delete(priv_vbi->decoder); priv_vbi->decoder = NULL; } if (priv_vbi->grabber_thread) pthread_join(priv_vbi->grabber_thread, NULL); pthread_mutex_destroy(&priv_vbi->buffer_mutex); if (priv_vbi->device){ free(priv_vbi->device); priv_vbi->device = NULL; } if (priv_vbi->errstr){ free(priv_vbi->errstr); priv_vbi->errstr = NULL; } if (priv_vbi->canvas){ free(priv_vbi->canvas); priv_vbi->canvas = NULL; } if (priv_vbi->txtpage){ free(priv_vbi->txtpage); priv_vbi->txtpage = NULL; } if (priv_vbi->network_name){ free(priv_vbi->network_name); priv_vbi->network_name = NULL; } if (priv_vbi->network_id){ free(priv_vbi->network_id); priv_vbi->network_id = NULL; } if (priv_vbi->page){ free(priv_vbi->page); priv_vbi->page = NULL; } if (priv_vbi->cache) { for (i = 0; i < 1000; i++) { if (priv_vbi->cache[i]) free(priv_vbi->cache[i]); } free(priv_vbi->cache); priv_vbi->cache = NULL; } free(priv_vbi); } /** * \brief Teletext control routine * \param priv_vbi private data structure * \param cmd command * \param arg command parameter (has to be not null) * */ int teletext_control(priv_vbi_t * priv_vbi, int cmd, void *arg) { vbi_page *page = NULL; char *txtpage = NULL; tv_teletext_img_t *img = NULL; if (!priv_vbi) return TVI_CONTROL_FALSE; if (!arg) return TVI_CONTROL_FALSE; switch (cmd) { case TVI_CONTROL_VBI_RESET: vbi_reset(priv_vbi); return TVI_CONTROL_TRUE; case TVI_CONTROL_VBI_START: vbi_start(priv_vbi); return TVI_CONTROL_TRUE; case TVI_CONTROL_VBI_GET_FORMAT: pthread_mutex_lock(&(priv_vbi->buffer_mutex)); *(int*)arg=priv_vbi->tformat; pthread_mutex_unlock(&(priv_vbi->buffer_mutex)); return TVI_CONTROL_TRUE; case TVI_CONTROL_VBI_SET_MODE: return teletext_set_mode(priv_vbi, *(int *) arg); case TVI_CONTROL_VBI_GET_MODE: pthread_mutex_lock(&(priv_vbi->buffer_mutex)); *(int*)arg=priv_vbi->on; pthread_mutex_unlock(&(priv_vbi->buffer_mutex)); return TVI_CONTROL_TRUE; case TVI_CONTROL_VBI_STEP_MODE: { int val; pthread_mutex_lock(&(priv_vbi->buffer_mutex)); val=(priv_vbi->on+*(int*)arg)%4; pthread_mutex_unlock(&(priv_vbi->buffer_mutex)); if (val<0) val+=4; return teletext_set_mode(priv_vbi,val); } case TVI_CONTROL_VBI_GET_HALF_PAGE: *(void **) arg = (void *) vbi_get_half(priv_vbi); return TVI_CONTROL_TRUE; case TVI_CONTROL_VBI_SET_HALF_PAGE: return teletext_set_half_page(priv_vbi, *(int *) arg); case TVI_CONTROL_VBI_STEP_HALF_PAGE: { int val; val=(vbi_get_half(priv_vbi)+*(int*)arg)%3; if (val<0) val+=3; return teletext_set_half_page(priv_vbi,val); } case TVI_CONTROL_VBI_SET_PAGE: vbi_setpage(priv_vbi, *(int *) arg, 0); return TVI_CONTROL_TRUE; case TVI_CONTROL_VBI_STEP_PAGE: vbi_steppage(priv_vbi, *(int *) arg); return TVI_CONTROL_TRUE; case TVI_CONTROL_VBI_ADD_DEC: vbi_add_dec(priv_vbi, *(char **) arg); return TVI_CONTROL_TRUE; case TVI_CONTROL_VBI_GO_LINK: return vbi_golink(priv_vbi, *(int *) arg); case TVI_CONTROL_VBI_GET_PAGE: *(int*) arg = priv_vbi->pgno; return TVI_CONTROL_TRUE; case TVI_CONTROL_VBI_GET_VBIPAGE: if (NULL == (page = vbi_getpage(priv_vbi))) return TVI_CONTROL_FALSE; *(void **) arg = (void *) page; return TVI_CONTROL_TRUE; case TVI_CONTROL_VBI_GET_TXTPAGE: if (NULL == (txtpage = vbi_getpagetext(priv_vbi))) return TVI_CONTROL_FALSE; *(void **) arg = (void *) txtpage; return TVI_CONTROL_TRUE; case TVI_CONTROL_VBI_GET_IMGPAGE: if (NULL == (img = vbi_getpageimg(priv_vbi))) return TVI_CONTROL_FALSE; *(void **) arg = (void *) img; return TVI_CONTROL_TRUE; } return TVI_CONTROL_UNKNOWN; }