comparison stream/tvi_vbi.c @ 23510:a6c619ee9d30

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
author voroshil
date Sun, 10 Jun 2007 00:06:12 +0000
parents
children
comparison
equal deleted inserted replaced
23509:53d57a0ebe13 23510:a6c619ee9d30
1 #include "config.h"
2
3 #include <stdlib.h>
4 #include <string.h>
5 #include <unistd.h>
6 #include <errno.h>
7
8 #include "tv.h"
9 #include "tvi_vbi.h"
10 #include "mp_msg.h"
11 #include "libmpcodecs/img_format.h"
12
13 #ifdef USE_ICONV
14 #include <iconv.h>
15 #endif
16
17 #define VBI_TEXT_CHARSET "UTF-8"
18
19 char* tv_param_tdevice=NULL; ///< teletext vbi device
20 char* tv_param_tformat="gray"; ///< format: text,bw,gray,color
21 int tv_param_tpage=100; ///< page number
22
23
24 #ifdef USE_ICONV
25 /*
26 ------------------------------------------------------------------
27 zvbi-0.2.25/src/exp-txt.c skip debug "if(1) fprintf(stderr,) " message
28 ------------------------------------------------------------------
29 */
30
31 /**
32 * libzvbi - Text export functions
33 *
34 * Copyright (C) 2001, 2002 Michael H. Schimek
35 *
36 * Based on code from AleVT 1.5.1
37 * Copyright (C) 1998, 1999 Edgar Toernig <froese@gmx.de>
38 *
39 * This program is free software; you can redistribute it and/or modify
40 * it under the terms of the GNU General Public License as published by
41 * the Free Software Foundation; either version 2 of the License, or
42 * (at your option) any later version.
43 *
44 * This program is distributed in the hope that it will be useful,
45 * but WITHOUT ANY WARRANTY; without even the implied warranty of
46 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
47 * GNU General Public License for more details.
48 *
49 * You should have received a copy of the GNU General Public License
50 * along with this program; if not, write to the Free Software
51 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
52 **/
53
54 /** $Id$ **/
55
56 static vbi_bool
57 print_unicode(iconv_t cd, int endian, int unicode, char **p, int n)
58 {
59 char in[2], *ip, *op;
60 size_t li, lo, r;
61
62 in[0 + endian] = unicode;
63 in[1 - endian] = unicode >> 8;
64 ip = in; op = *p;
65 li = sizeof(in); lo = n;
66
67 r = iconv(cd, &ip, &li, &op, &lo);
68
69 if ((size_t) -1 == r
70 || (**p == 0x40 && unicode != 0x0040)) {
71 in[0 + endian] = 0x20;
72 in[1 - endian] = 0;
73 ip = in; op = *p;
74 li = sizeof(in); lo = n;
75
76 r = iconv(cd, &ip, &li, &op, &lo);
77
78 if ((size_t) -1 == r
79 || (r == 1 && **p == 0x40))
80 goto error;
81 }
82
83 *p = op;
84
85 return TRUE;
86
87 error:
88 return FALSE;
89 }
90
91 static int
92 vbi_print_page_region_nodebug(vbi_page * pg, char *buf, int size,
93 const char *format, vbi_bool table,
94 vbi_bool rtl, int column, int row, int width,
95 int height)
96 {
97 int endian = vbi_ucs2be();
98 int column0, column1, row0, row1;
99 int x, y, spaces, doubleh, doubleh0;
100 iconv_t cd;
101 char *p;
102
103 rtl = rtl;
104
105 #if 0
106 if (1)
107 fprintf (stderr, "vbi_print_page_region '%s' "
108 "table=%d col=%d row=%d width=%d height=%d\n",
109 format, table, column, row, width, height);
110 #endif
111
112 column0 = column;
113 row0 = row;
114 column1 = column + width - 1;
115 row1 = row + height - 1;
116
117 if (!pg || !buf || size < 0 || !format
118 || column0 < 0 || column1 >= pg->columns
119 || row0 < 0 || row1 >= pg->rows
120 || endian < 0)
121 return 0;
122
123 if ((cd = iconv_open(format, "UCS-2")) == (iconv_t) -1)
124 return 0;
125
126 p = buf;
127
128 doubleh = 0;
129
130 for (y = row0; y <= row1; y++) {
131 int x0, x1, xl;
132
133 x0 = (table || y == row0) ? column0 : 0;
134 x1 = (table || y == row1) ? column1 : (pg->columns - 1);
135
136 xl = (table || y != row0 || (y + 1) != row1) ? -1 : column1;
137
138 doubleh0 = doubleh;
139
140 spaces = 0;
141 doubleh = 0;
142
143 for (x = x0; x <= x1; x++) {
144 vbi_char ac = pg->text[y * pg->columns + x];
145
146 if (table) {
147 if (ac.size > VBI_DOUBLE_SIZE)
148 ac.unicode = 0x0020;
149 } else {
150 switch (ac.size) {
151 case VBI_NORMAL_SIZE:
152 case VBI_DOUBLE_WIDTH:
153 break;
154
155 case VBI_DOUBLE_HEIGHT:
156 case VBI_DOUBLE_SIZE:
157 doubleh++;
158 break;
159
160 case VBI_OVER_TOP:
161 case VBI_OVER_BOTTOM:
162 continue;
163
164 case VBI_DOUBLE_HEIGHT2:
165 case VBI_DOUBLE_SIZE2:
166 if (y > row0)
167 ac.unicode = 0x0020;
168 break;
169 }
170
171 /*
172 * Special case two lines row0 ... row1, and all chars
173 * in row0, column0 ... column1 are double height: Skip
174 * row1, don't wrap around.
175 */
176 if (x == xl && doubleh >= (x - x0)) {
177 x1 = xl;
178 y = row1;
179 }
180
181 if (ac.unicode == 0x20 || !vbi_is_print(ac.unicode)) {
182 spaces++;
183 continue;
184 } else {
185 if (spaces < (x - x0) || y == row0) {
186 for (; spaces > 0; spaces--)
187 if (!print_unicode(cd, endian, 0x0020,
188 &p, buf + size - p))
189 goto failure;
190 } else /* discard leading spaces */
191 spaces = 0;
192 }
193 }
194
195 if (!print_unicode(cd, endian, ac.unicode, &p, buf + size - p))
196 goto failure;
197 }
198
199 /* if !table discard trailing spaces and blank lines */
200
201 if (y < row1) {
202 int left = buf + size - p;
203
204 if (left < 1)
205 goto failure;
206
207 if (table) {
208 *p++ = '\n'; /* XXX convert this (eg utf16) */
209 } else if (spaces >= (x1 - x0)) {
210 ; /* suppress blank line */
211 } else {
212 /* exactly one space between adjacent rows */
213 if (!print_unicode(cd, endian, 0x0020, &p, left))
214 goto failure;
215 }
216 } else {
217 if (doubleh0 > 0) {
218 ; /* prentend this is a blank double height lower row */
219 } else {
220 for (; spaces > 0; spaces--)
221 if (!print_unicode(cd, endian, 0x0020, &p, buf + size - p))
222 goto failure;
223 }
224 }
225 }
226
227 iconv_close(cd);
228 return p - buf;
229
230 failure:
231 iconv_close(cd);
232 return 0;
233 }
234 #endif
235 /*
236 end of zvbi-0.2.25/src/exp-txt.c part
237 */
238
239
240 /*
241 ------------------------------------------------------------------
242 Private routines
243 ------------------------------------------------------------------
244 */
245
246 /**
247 * \brief Decode event handler
248 * \param ev VBI event
249 * \param data pointer to user defined data
250 *
251 */
252 static void event_handler(vbi_event * ev, void *data)
253 {
254 priv_vbi_t *user_vbi = (priv_vbi_t *) data;
255 vbi_page pg;
256 char *s;
257 int i;
258
259 switch (ev->type) {
260 case VBI_EVENT_CAPTION:
261 mp_msg(MSGT_TV,MSGL_DBG3,"caption\n");
262 break;
263 case VBI_EVENT_NETWORK:
264 s = ev->ev.network.name;
265 if (s) {
266 pthread_mutex_lock(&(user_vbi->buffer_mutex));
267 if (user_vbi->network_name)
268 free(user_vbi->network_name);
269 user_vbi->network_name = strdup(s);
270 pthread_mutex_unlock(&(user_vbi->buffer_mutex));
271 }
272 break;
273 case VBI_EVENT_NETWORK_ID:
274 s = ev->ev.network.name;
275 if (s) {
276 pthread_mutex_lock(&(user_vbi->buffer_mutex));
277 if (user_vbi->network_id)
278 free(user_vbi->network_id);
279 user_vbi->network_id = strdup(s);
280 pthread_mutex_unlock(&(user_vbi->buffer_mutex));
281 }
282 break;
283 case VBI_EVENT_TTX_PAGE:
284 pthread_mutex_lock(&(user_vbi->buffer_mutex));
285 user_vbi->curr_pgno = ev->ev.ttx_page.pgno; // page number
286 user_vbi->curr_subno = ev->ev.ttx_page.subno; // subpage
287 i = vbi_bcd2dec(ev->ev.ttx_page.pgno);
288 if (i > 0 && i < 1000) {
289 if (!user_vbi->cache[i])
290 user_vbi->cache[i] = (vbi_page *) malloc(sizeof(vbi_page));
291 vbi_fetch_vt_page(user_vbi->decoder, // fetch page
292 user_vbi->cache[i],
293 ev->ev.ttx_page.pgno,
294 ev->ev.ttx_page.subno,
295 VBI_WST_LEVEL_3p5, 25, TRUE);
296 memcpy(user_vbi->theader, user_vbi->cache[i]->text,
297 sizeof(user_vbi->theader));
298 }
299 pthread_mutex_unlock(&(user_vbi->buffer_mutex));
300 break;
301 }
302 }
303
304 /**
305 * \brief Prepares page to be shown on screen
306 * \param priv_vbi private data structure
307 *
308 * This routine adds page number, current time, etc to page header
309 *
310 */
311 static void process_page(priv_vbi_t * priv_vbi)
312 {
313 char *pagesptr;
314 int csize, i, j, subtitle = 0, sflg, send;
315 void *canvas;
316 char cpage[5];
317 vbi_page page;
318
319 memcpy(&(page), priv_vbi->page, sizeof(vbi_page));
320 if (priv_vbi->pgno != priv_vbi->page->pgno) {
321 //don't clear first line
322 for (i = page.columns; i < 1056; i++) {
323 page.text[i].unicode = ' ';
324 page.text[i].background = VBI_TRANSPARENT_COLOR;
325 }
326 snprintf(cpage, sizeof(cpage), "%03X", priv_vbi->pgno);
327 page.text[1].unicode = cpage[0];
328 page.text[2].unicode = cpage[1];
329 page.text[3].unicode = cpage[2];
330 page.text[4].unicode = ' ';
331 page.text[5].unicode = ' ';
332 page.text[6].unicode = ' ';
333 }
334
335 //background page number & title
336 j=vbi_bcd2dec(priv_vbi->curr_pgno);
337 if (j>0 && j<1000 && priv_vbi->cache[j]){
338 for(i=8;i<priv_vbi->cache[j]->columns;i++){
339 page.text[i].unicode = priv_vbi->cache[j]->text[i].unicode;
340 }
341 }
342
343 if (page.text[1].unicode == ' ' && page.text[2].unicode == ' ' &&
344 page.text[3].unicode == ' ' && page.text[4].unicode == ' ' &&
345 page.text[5].unicode == ' ' && page.text[5].unicode == ' '
346 && !priv_vbi->half)
347 subtitle = 1; // subtitle page
348 if (priv_vbi->pagenumdec) {
349 i = (priv_vbi->pagenumdec >> 12) & 0xf;
350 switch (i) {
351 case 1:
352 page.text[1].unicode = '0' + ((priv_vbi->pagenumdec >> 0) & 0xf);
353 page.text[2].unicode = '-';
354 page.text[3].unicode = '-';
355 break;
356 case 2:
357 page.text[1].unicode = '0' + ((priv_vbi->pagenumdec >> 4) & 0xf);
358 page.text[2].unicode = '0' + ((priv_vbi->pagenumdec >> 0) & 0xf);
359 page.text[3].unicode = '-';
360 break;
361 }
362 page.text[4].unicode = ' ';
363 page.text[5].unicode = ' ';
364 page.text[6].unicode = ' ';
365 page.text[1].foreground = VBI_WHITE;
366 page.text[2].foreground = VBI_WHITE;
367 page.text[3].foreground = VBI_WHITE;
368 }
369 priv_vbi->columns = page.columns;
370 priv_vbi->rows = page.rows;
371 if (!subtitle) { // update time in header
372 memcpy(&(page.text[VBI_TIME_LINEPOS]),
373 &(priv_vbi->theader[VBI_TIME_LINEPOS]),
374 sizeof(vbi_char) * (priv_vbi->columns - VBI_TIME_LINEPOS));
375 }
376 switch (priv_vbi->tformat) {
377 case VBI_TFORMAT_TEXT: // mode: text
378 if (priv_vbi->txtpage) {
379 #ifdef USE_ICONV
380 vbi_print_page_region_nodebug(&(page), priv_vbi->txtpage,
381 VBI_TXT_PAGE_SIZE, VBI_TEXT_CHARSET, TRUE,
382 0, 0, 0, page.columns, page.rows); // vbi_page to text without message
383 #else
384 vbi_print_page(&(page), priv_vbi->txtpage,
385 VBI_TXT_PAGE_SIZE, VBI_TEXT_CHARSET, TRUE, 0);
386 #endif
387 }
388 priv_vbi->valid_page = 1;
389 break;
390 case VBI_TFORMAT_BW: // mode: black & white
391 for (i=0; i < (priv_vbi->pgno!=page.pgno?page.columns:1056); i++) {
392 if (priv_vbi->foreground){
393 page.text[i].foreground = VBI_BLACK;
394 page.text[i].background = VBI_WHITE;
395 }else{
396 page.text[i].foreground = VBI_WHITE;
397 page.text[i].background = VBI_BLACK;
398 }
399 }
400 case VBI_TFORMAT_GRAY: // mode: grayscale
401 case VBI_TFORMAT_COLOR: // mode: color (request color spu patch!)
402
403
404
405 page.color_map[VBI_TRANSPARENT_COLOR] = 0;
406 if (priv_vbi->alpha) {
407 if (subtitle) {
408 for (i = 0; i < page.rows; i++) {
409 sflg = 0;
410 send = 0;
411 for (j = 0; j < page.columns; j++) {
412 if (page.text[i * page.columns + j].unicode != ' ') {
413 sflg = 1;
414 send = j;
415 }
416 if (sflg == 0)
417 page.text[i * page.columns + j].background =
418 VBI_TRANSPARENT_COLOR;
419 }
420 for (j = send + 1; j < page.columns; j++)
421 page.text[i * page.columns + j].background =
422 VBI_TRANSPARENT_COLOR;
423 }
424 } else {
425 for (i = 0; i < 1056; i++)
426 page.text[i].background = VBI_TRANSPARENT_COLOR;
427 }
428 }
429 csize = page.columns * page.rows * 12 * 10 * sizeof(vbi_rgba);
430 if (csize == 0)
431 break;
432 if (csize > priv_vbi->canvas_size) { // test canvas size
433 if (priv_vbi->canvas)
434 free(priv_vbi->canvas);
435 priv_vbi->canvas = malloc(csize);
436 priv_vbi->canvas_size = 0;
437 if (priv_vbi->canvas)
438 priv_vbi->canvas_size = csize;
439 }
440 if (priv_vbi->canvas) {
441 vbi_draw_vt_page(&(page),
442 priv_vbi->fmt,
443 priv_vbi->canvas,
444 priv_vbi->reveal, priv_vbi->flash_on);
445 priv_vbi->csize = csize;
446 }
447 priv_vbi->spudec_proc = 1;
448 priv_vbi->valid_page = 1;
449 break;
450 }
451 }
452
453 /**
454 * \brief Update page in cache
455 * \param priv_vbi private data structure
456 *
457 * Routine also calls process_page to refresh currently visible page (if so)
458 * every time it was received from VBI by background thread.
459 *
460 */
461 static void update_page(priv_vbi_t * priv_vbi)
462 {
463 int i;
464 int index;
465 pthread_mutex_lock(&(priv_vbi->buffer_mutex));
466 /*
467 priv_vbi->redraw=1 - page redraw requested
468 pgno!=page->pgno - page was switched
469 curr_pgno==pgno - backgound process just fetched current page, refresh it
470 */
471 if (priv_vbi->redraw ||
472 priv_vbi->pgno != priv_vbi->page->pgno ||
473 priv_vbi->curr_pgno == priv_vbi->pgno) {
474 index = vbi_bcd2dec(priv_vbi->pgno);
475 if ( index <= 0 || index > 999 || !priv_vbi->cache[index]) {
476 // curr_pgno is last decoded page
477 index = vbi_bcd2dec(priv_vbi->curr_pgno);
478 }
479
480 if (index <=0 || index >999 || !priv_vbi->cache[index]){
481 priv_vbi->valid_page = 0;
482 memset(priv_vbi->page, 0, sizeof(vbi_page));
483 }else
484 {
485 memcpy(priv_vbi->page, priv_vbi->cache[index], sizeof(vbi_page));
486 process_page(priv_vbi);//prepare page to be shown on screen
487 }
488 }
489 pthread_mutex_unlock(&(priv_vbi->buffer_mutex));
490 }
491
492 /**
493 * \brief background grabber routine
494 * \param data user-defined data
495 *
496 */
497 static void *grabber(void *data)
498 {
499 priv_vbi_t *user_vbi = (priv_vbi_t *) data;
500 vbi_capture_buffer *sliced_buffer;
501 struct timeval timeout;
502 unsigned int n_lines;
503 int r, err_count = 0;
504
505 while (!user_vbi->eof) {
506 timeout.tv_sec = 0;
507 timeout.tv_usec = 500;
508 r = vbi_capture_pull(user_vbi->capture, NULL, &sliced_buffer, &timeout); // grab slices
509 if (user_vbi->eof)
510 return NULL;
511 switch (r) {
512 case -1: // read error
513 if (err_count++ > 4)
514 user_vbi->eof = 1;
515 break;
516 case 0: // time out
517 break;
518 default:
519 err_count = 0;
520 }
521 if (r != 1)
522 continue;
523 n_lines = sliced_buffer->size / sizeof(vbi_sliced);
524 vbi_decode(user_vbi->decoder, (vbi_sliced *) sliced_buffer->data,
525 n_lines, sliced_buffer->timestamp); // decode slice
526 update_page(user_vbi);
527 }
528 switch (r) {
529 case -1:
530 mp_msg(MSGT_TV, MSGL_ERR, "VBI read error %d (%s)\n",
531 errno, strerror(errno));
532 return NULL;
533 case 0:
534 mp_msg(MSGT_TV, MSGL_ERR, "VBI read timeout\n");
535 return NULL;
536 }
537 return NULL;
538 }
539
540 /**
541 * \brief calculate increased/decreased by given value page number
542 * \param curr current page number in hexadecimal for
543 * \param direction decimal value (can be negative) to add to value or curr parameter
544 * \return new page number in hexadecimal form
545 *
546 * VBI page numbers are represented in special hexadecimal form, e.g.
547 * page with number 123 (as seen by user) internally has number 0x123.
548 * and equation 0x123+8 should be equal to 0x131 instead of regular 0x12b.
549 * Page numbers 0xYYY (where Y is not belongs to (0..9) and pages below 0x100 and
550 * higher 0x999 are reserved for internal use.
551 *
552 */
553 static int steppage(int curr, int direction)
554 {
555 int newpage = vbi_dec2bcd(vbi_bcd2dec(curr) + direction);
556 if (newpage < 0x100)
557 newpage = 0x100;
558 if (newpage > 0x999)
559 newpage = 0x999;
560 return newpage;
561 }
562
563 /**
564 * \brief toggles teletext page displaying mode
565 * \param priv_vbi private data structure
566 * \param flag new mode
567 * \return
568 * TVI_CONTROL_TRUE is success,
569 * TVI_CONTROL_FALSE otherwise
570 *
571 * flag:
572 * 0 - off
573 * 1 - on & opaque
574 * 2 - on & transparent
575 * 3 - on & transparent with black foreground color (only in bw mode)
576 *
577 */
578 static int teletext_set_mode(priv_vbi_t * priv_vbi, int flag)
579 {
580 if (flag<0 || flag>3)
581 return TVI_CONTROL_FALSE;
582
583 pthread_mutex_lock(&(priv_vbi->buffer_mutex));
584
585 priv_vbi->on = flag;
586
587 if (priv_vbi->on > 2 && priv_vbi->tformat != VBI_TFORMAT_BW)
588 priv_vbi->on = 0;
589
590 priv_vbi->foreground = 0;
591 priv_vbi->pagenumdec = 0;
592 priv_vbi->spudec_proc = 1;
593 priv_vbi->redraw = 1;
594 switch (priv_vbi->on) {
595 case 0:
596 priv_vbi->csize = 0;
597 break;
598 case 1:
599 priv_vbi->alpha = 0;
600 break;
601 case 2:
602 priv_vbi->alpha = 1;
603 break;
604 case 3:
605 priv_vbi->alpha = 1;
606 priv_vbi->foreground = 1;
607 break;
608 }
609 pthread_mutex_unlock(&(priv_vbi->buffer_mutex));
610 return TVI_CONTROL_TRUE;
611 }
612
613 /**
614 * \brief get half page mode (only in SPU mode)
615 * \param priv_vbi private data structure
616 * \return current mode
617 * 0 : half mode off
618 * 1 : top half page
619 * 2 : bottom half page
620 */
621 static int vbi_get_half(priv_vbi_t * priv_vbi)
622 {
623 int flag = 0;
624 pthread_mutex_lock(&(priv_vbi->buffer_mutex));
625 if (priv_vbi->valid_page)
626 flag = priv_vbi->half;
627 priv_vbi->pagenumdec = 0;
628 pthread_mutex_unlock(&(priv_vbi->buffer_mutex));
629 return flag;
630 }
631
632 /**
633 * \brief set half page mode (only in SPU mode)
634 * \param priv_vbi private data structure
635 * \param flag new half page mode
636 * \return
637 * TVI_CONTROL_TRUE is success,
638 * TVI_CONTROL_FALSE otherwise
639 *
640 *
641 * flag:
642 * 0 : half mode off
643 * 1 : top half page
644 * 2 : bottom half page
645 */
646 static int teletext_set_half_page(priv_vbi_t * priv_vbi, int flag)
647 {
648 if (flag<0 || flag>2)
649 return TVI_CONTROL_FALSE;
650
651 pthread_mutex_lock(&(priv_vbi->buffer_mutex));
652 priv_vbi->half = flag;
653 if (priv_vbi->tformat == VBI_TFORMAT_TEXT && priv_vbi->half > 1)
654 priv_vbi->half = 0;
655 priv_vbi->redraw = 1;
656 priv_vbi->pagenumdec = 0;
657 pthread_mutex_unlock(&(priv_vbi->buffer_mutex));
658 return TVI_CONTROL_TRUE;
659 }
660
661 /**
662 * \brief displays specified page
663 * \param priv_vbi private data structure
664 * \param pgno page number to display
665 * \param subno subpage number
666 *
667 */
668 static void vbi_setpage(priv_vbi_t * priv_vbi, int pgno, int subno)
669 {
670 pthread_mutex_lock(&(priv_vbi->buffer_mutex));
671 priv_vbi->pgno = steppage(0, pgno);
672 priv_vbi->subno = subno;
673 priv_vbi->redraw = 1;
674 priv_vbi->pagenumdec = 0;
675 pthread_mutex_unlock(&(priv_vbi->buffer_mutex));
676 }
677
678 /**
679 * \brief steps over pages by a given value
680 * \param priv_vbi private data structure
681 * \param direction decimal step value (can be negative)
682 *
683 */
684 static void vbi_steppage(priv_vbi_t * priv_vbi, int direction)
685 {
686 pthread_mutex_lock(&(priv_vbi->buffer_mutex));
687 priv_vbi->pgno = steppage(priv_vbi->pgno, direction);
688 priv_vbi->redraw = 1;
689 priv_vbi->pagenumdec = 0;
690 pthread_mutex_unlock(&(priv_vbi->buffer_mutex));
691 }
692
693 /**
694 * \brief append just entered digit to editing page number
695 * \param priv_vbi private data structure
696 * \param dec decimal digit to append
697 *
698 * dec:
699 * '0'..'9' append digit
700 * '-' remove last digit (backspace emulation)
701 *
702 * This routine allows user to jump to arbitrary page.
703 * It implements simple page number editing algorithm.
704 *
705 * Subsystem can be on one of two modes: normal and page number edit mode.
706 * Zero value of priv_vbi->pagenumdec means normal mode
707 * Non-zero value means page number edit mode and equals to packed
708 * decimal number of already entered part of page number.
709 *
710 * How this works.
711 * Let's assume that current mode is normal (pagenumdec is zero), teletext page
712 * 100 are displayed as usual. topmost left corner of page contains page number.
713 * Then vbi_add_dec is sequentally called (through slave
714 * command of course) with 1,4,-,2,3 * values of dec parameter.
715 *
716 * +-----+------------+------------------+
717 * | dec | pagenumxec | displayed number |
718 * +-----+------------+------------------+
719 * | | 0x000 | 100 |
720 * +-----+------------+------------------+
721 * | 1 | 0x001 | __1 |
722 * +-----+------------+------------------+
723 * | 4 | 0x014 | _14 |
724 * +-----+------------+------------------+
725 * | - | 0x001 | __1 |
726 * +-----+------------+------------------+
727 * | 2 | 0x012 | _12 |
728 * +-----+------------+------------------+
729 * | 3 | 0x123 | 123 |
730 * +-----+------------+------------------+
731 * | | 0x000 | 123 |
732 * +-----+------------+------------------+
733 *
734 * pagenumdec will automatically receive zero value after third digit of page number
735 * is entered and current page will be switched to another one with entered page number.
736 *
737 */
738 static void vbi_add_dec(priv_vbi_t * priv_vbi, char *dec)
739 {
740 int count, shift;
741 if (!dec)
742 return;
743 if (!priv_vbi->on)
744 return;
745 if ((*dec < '0' || *dec > '9') && *dec != '-')
746 return;
747 pthread_mutex_lock(&(priv_vbi->buffer_mutex));
748 count = (priv_vbi->pagenumdec >> 12) & 0xf;
749 if (*dec == '-') {
750 count--;
751 if (count)
752 priv_vbi->pagenumdec = ((priv_vbi->pagenumdec >> 4) & 0xfff) | (count << 12);
753 else
754 priv_vbi->pagenumdec = 0;
755 } else {
756 shift = count * 4;
757 count++;
758 priv_vbi->pagenumdec =
759 (((priv_vbi->pagenumdec) << 4 | (*dec -'0')) & 0xfff) | (count << 12);
760 if (count == 3) {
761 priv_vbi->pgno = priv_vbi->pagenumdec & 0xfff;
762 priv_vbi->subno = 0;
763 priv_vbi->redraw = 1;
764 priv_vbi->pagenumdec = 0;
765 }
766 }
767 pthread_mutex_unlock(&(priv_vbi->buffer_mutex));
768 }
769
770 /**
771 * \brief follows link specified on current page
772 * \param priv_vbi private data structure
773 * \param linkno link number (0..6)
774 * \return
775 * TVI_CONTROL_FALSE if linkno is outside 0..6 range or if
776 * teletext is switched off
777 * TVI_CONTROL_TRUE otherwise
778 *
779 * linkno:
780 * 0: tpage in tv parameters (starting page, usually 100)
781 * 1..6: follows link on current page with given number
782 *
783 * FIXME: quick test shows that this is working strange
784 * FIXME: routine does not checks whether links exists on page or not
785 * TODO: more precise look
786 *
787 */
788 static int vbi_golink(priv_vbi_t * priv_vbi, int linkno)
789 {
790 if (linkno < 0 || linkno > 6)
791 return TVI_CONTROL_FALSE;
792 if (!priv_vbi->on)
793 return TVI_CONTROL_FALSE;
794 pthread_mutex_lock(&(priv_vbi->buffer_mutex));
795 if (linkno == 0) {
796 priv_vbi->pgno = priv_vbi->tpage;
797 priv_vbi->subno = priv_vbi->page->nav_link[linkno].subno;
798 priv_vbi->redraw = 1;
799 priv_vbi->pagenumdec = 0;
800 } else {
801 linkno--;
802 if (priv_vbi->pgno == priv_vbi->page->pgno) {
803 priv_vbi->pgno = priv_vbi->page->nav_link[linkno].pgno;
804 priv_vbi->subno = priv_vbi->page->nav_link[linkno].subno;
805 priv_vbi->redraw = 1;
806 priv_vbi->pagenumdec = 0;
807 }
808 }
809 priv_vbi->pagenumdec = 0;
810 pthread_mutex_unlock(&(priv_vbi->buffer_mutex));
811 return TVI_CONTROL_TRUE;
812 }
813
814 /**
815 * \brief get pointer to current teletext page
816 * \param priv_vbi private data structure
817 * \return pointer to vbi_page structure if teletext is
818 * switched on and current page is valid, NULL - otherwise
819 *
820 */
821 static vbi_page *vbi_getpage(priv_vbi_t * priv_vbi)
822 {
823 vbi_page *page = NULL;
824
825 if (!priv_vbi->on)
826 return NULL;
827 pthread_mutex_lock(&(priv_vbi->buffer_mutex));
828 if (priv_vbi->valid_page)
829 if (page = malloc(sizeof(vbi_page)))
830 memcpy(page, priv_vbi->page, sizeof(vbi_page));
831 pthread_mutex_unlock(&(priv_vbi->buffer_mutex));
832 return page;
833 }
834
835 /**
836 * \brief get pointer to current teletext page
837 * \param priv_vbi private data structure
838 * \return pointer to character string, containing text-only data of
839 * teletext page. If teletext is switched off, current page is invalid
840 * or page format if not equal to "text" then returning value is NULL.
841 *
842 */
843 static char *vbi_getpagetext(priv_vbi_t * priv_vbi)
844 {
845 char *page = NULL;
846
847 if (!priv_vbi->on)
848 return NULL;
849 if (priv_vbi->tformat != VBI_TFORMAT_TEXT && priv_vbi->canvas)
850 return NULL;
851 pthread_mutex_lock(&(priv_vbi->buffer_mutex));
852 if (priv_vbi->valid_page)
853 page = priv_vbi->txtpage;
854 if (!page)
855 page = priv_vbi->header;
856 pthread_mutex_unlock(&(priv_vbi->buffer_mutex));
857 return page;
858 }
859
860 /**
861 * \brief get current page RGBA32 image (only in SPU mode)
862 * \param priv_vbi private data structure
863 * \return pointer to tv_teletext_img_t structure, containing among
864 * other things rendered RGBA32 image of current teletext page.
865 * return NULL is image is not available for some reason.
866 *
867 */
868 static tv_teletext_img_t *vbi_getpageimg(priv_vbi_t * priv_vbi)
869 {
870 tv_teletext_img_t *img = NULL;
871
872 if (priv_vbi->tformat == VBI_TFORMAT_TEXT)
873 return NULL;
874 if (priv_vbi->spudec_proc == 0)
875 return NULL;
876 pthread_mutex_lock(&(priv_vbi->buffer_mutex));
877 if (NULL != (img = malloc(sizeof(tv_teletext_img_t)))) {
878 img->tformat = priv_vbi->tformat; // format: bw|gray|color
879 img->tformat = VBI_TFORMAT_GRAY; // format: bw|gray|color
880 img->half = priv_vbi->half; // half mode
881 img->columns = priv_vbi->columns; // page size
882 img->rows = priv_vbi->rows;
883 img->width = priv_vbi->columns * 12;
884 img->width = priv_vbi->rows * 10;
885 img->canvas = NULL;
886 // is page ok?
887 if (priv_vbi->canvas && priv_vbi->on && priv_vbi->csize && priv_vbi->valid_page) {
888
889 if (NULL != (img->canvas = malloc(priv_vbi->csize)))
890 memcpy(img->canvas, priv_vbi->canvas, priv_vbi->csize);
891 }
892 }
893 priv_vbi->spudec_proc = 0;
894 pthread_mutex_unlock(&(priv_vbi->buffer_mutex));
895 return img;
896 }
897
898 /**
899 * \brief start teletext sybsystem
900 * \param priv_vbi private data structure
901 *
902 * initializes cache, vbi decoder and starts background thread
903 *
904 */
905 static void vbi_start(priv_vbi_t * priv_vbi)
906 {
907 if (!priv_vbi)
908 return;
909 if (NULL != (priv_vbi->txtpage = malloc(VBI_TXT_PAGE_SIZE))) // alloc vbi_page
910 memset(priv_vbi->txtpage, 0, VBI_TXT_PAGE_SIZE);
911 priv_vbi->page = malloc(sizeof(vbi_page));
912 priv_vbi->cache = (vbi_page **) malloc(1000 * sizeof(vbi_page *));
913 memset(priv_vbi->cache, 0, 1000 * sizeof(vbi_page *));
914 priv_vbi->decoder = vbi_decoder_new();
915 priv_vbi->subno = 0;
916 priv_vbi->fmt = VBI_PIXFMT_RGBA32_LE;
917 memset(priv_vbi->theader, 0, sizeof(priv_vbi->theader));
918 snprintf(priv_vbi->header, sizeof(priv_vbi->header), "%s", VBI_NO_TELETEXT);
919 vbi_event_handler_add(priv_vbi->decoder, ~0, event_handler, (void *) priv_vbi); // add event handler
920 pthread_create(&priv_vbi->grabber_thread, NULL, grabber, priv_vbi); // add grab function
921 pthread_mutex_init(&priv_vbi->buffer_mutex, NULL);
922 priv_vbi->valid_page = 0;
923 priv_vbi->pagenumdec = 0;
924 mp_msg(MSGT_TV, MSGL_INFO, "Teletext device: %s\n", priv_vbi->device);
925 }
926
927 /**
928 * \brief Teletext reset
929 * \param priv_vbi private data structure
930 *
931 * should be called during frequency, norm change, etc
932 *
933 */
934 static void vbi_reset(priv_vbi_t * priv_vbi)
935 {
936 int i;
937 pthread_mutex_lock(&(priv_vbi->buffer_mutex));
938 if (priv_vbi->canvas)
939 free(priv_vbi->canvas);
940 priv_vbi->canvas = NULL;
941 priv_vbi->canvas_size = 0;
942 priv_vbi->redraw = 1;
943 priv_vbi->csize = 0;
944 priv_vbi->valid_page = 0;
945 priv_vbi->spudec_proc = 1;
946 priv_vbi->pagenumdec = 0;
947 if (priv_vbi->page)
948 memset(priv_vbi->page, 0, sizeof(vbi_page));
949 if (priv_vbi->txtpage)
950 memset(priv_vbi->txtpage, 0, VBI_TXT_PAGE_SIZE);
951 memset(priv_vbi->theader, 0, sizeof(priv_vbi->theader));
952 if (priv_vbi->cache) {
953 for (i = 0; i < 1000; i++) {
954 if (priv_vbi->cache[i])
955 free(priv_vbi->cache[i]);
956 priv_vbi->cache[i] = NULL;
957 }
958 }
959 snprintf(priv_vbi->header, sizeof(priv_vbi->header), "%s",
960 VBI_NO_TELETEXT);
961 pthread_mutex_unlock(&(priv_vbi->buffer_mutex));
962 }
963
964 /*
965 ---------------------------------------------------------------------------------
966 Public routines
967 ---------------------------------------------------------------------------------
968 */
969
970 /**
971 * \brief teletext subsystem init
972 * \note Routine uses global variables tv_param_tdevice, tv_param_tpage
973 * and tv_param_tformat for initialization.
974 *
975 */
976 priv_vbi_t *teletext_init(void)
977 {
978 priv_vbi_t *priv_vbi;
979 int formatid, startpage;
980 unsigned int services = VBI_SLICED_TELETEXT_B |
981 VBI_SLICED_CAPTION_525 |
982 VBI_SLICED_CAPTION_625 |
983 VBI_SLICED_VBI_525 |
984 VBI_SLICED_VBI_625 |
985 VBI_SLICED_WSS_625 |
986 VBI_SLICED_WSS_CPR1204 |
987 VBI_SLICED_VPS;
988
989 if (!tv_param_tdevice)
990 return NULL;
991
992 if (NULL == (priv_vbi = malloc(sizeof(priv_vbi_t))))
993 return NULL;
994 memset(priv_vbi, 0, sizeof(priv_vbi_t));
995 formatid = VBI_TFORMAT_TEXT; // default
996 if (tv_param_tformat != NULL) {
997 if (strcmp(tv_param_tformat, "text") == 0)
998 formatid = VBI_TFORMAT_TEXT;
999 if (strcmp(tv_param_tformat, "bw") == 0)
1000 formatid = VBI_TFORMAT_BW;
1001 if (strcmp(tv_param_tformat, "gray") == 0)
1002 formatid = VBI_TFORMAT_GRAY;
1003 if (strcmp(tv_param_tformat, "color") == 0)
1004 formatid = VBI_TFORMAT_COLOR;
1005 }
1006 startpage = steppage(0, tv_param_tpage); // page number is HEX
1007 if (startpage < 0x100 || startpage > 0x999)
1008 startpage = 0x100;
1009 priv_vbi->device = strdup(tv_param_tdevice);
1010 priv_vbi->tformat = formatid;
1011 priv_vbi->tpage = startpage; // page number
1012 priv_vbi->pgno = startpage; // page number
1013
1014
1015 if (!priv_vbi->capture) {
1016 priv_vbi->services = services; // probe v4l2
1017 priv_vbi->capture = vbi_capture_v4l2_new(priv_vbi->device, // device
1018 20, // buffer numbers
1019 &(priv_vbi->services), // services
1020 0, // strict
1021 &(priv_vbi->errstr), // error string
1022 0); // trace
1023 }
1024 services = priv_vbi->services;
1025 if (priv_vbi->capture == NULL) {
1026 priv_vbi->services = services; // probe v4l
1027 priv_vbi->capture = vbi_capture_v4l_new(priv_vbi->device,
1028 20,
1029 &(priv_vbi->services),
1030 0, &(priv_vbi->errstr), 0);
1031 }
1032
1033 if (!priv_vbi->capture) {
1034 free(priv_vbi->device);
1035 free(priv_vbi);
1036 mp_msg(MSGT_TV, MSGL_INFO, "No teletext\n");
1037 return NULL;
1038 }
1039 return priv_vbi;
1040 }
1041
1042 /**
1043 * \brief teletext subsystem uninitialization
1044 * \param priv_vbi private data structure
1045 *
1046 * closes vbi capture, decode and and frees priv_vbi structure
1047 *
1048 */
1049 void teletext_uninit(priv_vbi_t * priv_vbi)
1050 {
1051 int i;
1052 if (priv_vbi == NULL)
1053 return;
1054 priv_vbi->eof = 1;
1055 if (priv_vbi->capture){
1056 vbi_capture_delete(priv_vbi->capture);
1057 priv_vbi->capture = NULL;
1058 }
1059 if (priv_vbi->decoder){
1060 vbi_event_handler_remove(priv_vbi->decoder, event_handler);
1061 vbi_decoder_delete(priv_vbi->decoder);
1062 priv_vbi->decoder = NULL;
1063 }
1064 if (priv_vbi->grabber_thread)
1065 pthread_join(priv_vbi->grabber_thread, NULL);
1066 pthread_mutex_destroy(&priv_vbi->buffer_mutex);
1067 if (priv_vbi->device){
1068 free(priv_vbi->device);
1069 priv_vbi->device = NULL;
1070 }
1071 if (priv_vbi->errstr){
1072 free(priv_vbi->errstr);
1073 priv_vbi->errstr = NULL;
1074 }
1075 if (priv_vbi->canvas){
1076 free(priv_vbi->canvas);
1077 priv_vbi->canvas = NULL;
1078 }
1079 if (priv_vbi->txtpage){
1080 free(priv_vbi->txtpage);
1081 priv_vbi->txtpage = NULL;
1082 }
1083 if (priv_vbi->network_name){
1084 free(priv_vbi->network_name);
1085 priv_vbi->network_name = NULL;
1086 }
1087 if (priv_vbi->network_id){
1088 free(priv_vbi->network_id);
1089 priv_vbi->network_id = NULL;
1090 }
1091 if (priv_vbi->page){
1092 free(priv_vbi->page);
1093 priv_vbi->page = NULL;
1094 }
1095 if (priv_vbi->cache) {
1096 for (i = 0; i < 1000; i++) {
1097 if (priv_vbi->cache[i])
1098 free(priv_vbi->cache[i]);
1099 }
1100 free(priv_vbi->cache);
1101 priv_vbi->cache = NULL;
1102 }
1103 free(priv_vbi);
1104 }
1105
1106 /**
1107 * \brief Teletext control routine
1108 * \param priv_vbi private data structure
1109 * \param cmd command
1110 * \param arg command parameter (has to be not null)
1111 *
1112 */
1113 int teletext_control(priv_vbi_t * priv_vbi, int cmd, void *arg)
1114 {
1115 vbi_page *page = NULL;
1116 char *txtpage = NULL;
1117 tv_teletext_img_t *img = NULL;
1118 if (!priv_vbi)
1119 return TVI_CONTROL_FALSE;
1120 if (!arg)
1121 return TVI_CONTROL_FALSE;
1122 switch (cmd) {
1123 case TVI_CONTROL_VBI_RESET:
1124 vbi_reset(priv_vbi);
1125 return TVI_CONTROL_TRUE;
1126 case TVI_CONTROL_VBI_START:
1127 vbi_start(priv_vbi);
1128 return TVI_CONTROL_TRUE;
1129 case TVI_CONTROL_VBI_GET_FORMAT:
1130 pthread_mutex_lock(&(priv_vbi->buffer_mutex));
1131 *(int*)arg=priv_vbi->tformat;
1132 pthread_mutex_unlock(&(priv_vbi->buffer_mutex));
1133 return TVI_CONTROL_TRUE;
1134 case TVI_CONTROL_VBI_SET_MODE:
1135 return teletext_set_mode(priv_vbi, *(int *) arg);
1136 case TVI_CONTROL_VBI_GET_MODE:
1137 pthread_mutex_lock(&(priv_vbi->buffer_mutex));
1138 *(int*)arg=priv_vbi->on;
1139 pthread_mutex_unlock(&(priv_vbi->buffer_mutex));
1140 return TVI_CONTROL_TRUE;
1141 case TVI_CONTROL_VBI_STEP_MODE:
1142 {
1143 int val;
1144 pthread_mutex_lock(&(priv_vbi->buffer_mutex));
1145 val=(priv_vbi->on+*(int*)arg)%4;
1146 pthread_mutex_unlock(&(priv_vbi->buffer_mutex));
1147 if (val<0)
1148 val+=4;
1149 return teletext_set_mode(priv_vbi,val);
1150 }
1151 case TVI_CONTROL_VBI_GET_HALF_PAGE:
1152 *(void **) arg = (void *) vbi_get_half(priv_vbi);
1153 return TVI_CONTROL_TRUE;
1154 case TVI_CONTROL_VBI_SET_HALF_PAGE:
1155 return teletext_set_half_page(priv_vbi, *(int *) arg);
1156 case TVI_CONTROL_VBI_STEP_HALF_PAGE:
1157 {
1158 int val;
1159 val=(vbi_get_half(priv_vbi)+*(int*)arg)%3;
1160
1161 if (val<0)
1162 val+=3;
1163 return teletext_set_half_page(priv_vbi,val);
1164 }
1165
1166 case TVI_CONTROL_VBI_SET_PAGE:
1167 vbi_setpage(priv_vbi, *(int *) arg, 0);
1168 return TVI_CONTROL_TRUE;
1169 case TVI_CONTROL_VBI_STEP_PAGE:
1170 vbi_steppage(priv_vbi, *(int *) arg);
1171 return TVI_CONTROL_TRUE;
1172 case TVI_CONTROL_VBI_ADD_DEC:
1173 vbi_add_dec(priv_vbi, *(char **) arg);
1174 return TVI_CONTROL_TRUE;
1175 case TVI_CONTROL_VBI_GO_LINK:
1176 return vbi_golink(priv_vbi, *(int *) arg);
1177 case TVI_CONTROL_VBI_GET_PAGE:
1178 *(int*) arg = priv_vbi->pgno;
1179 return TVI_CONTROL_TRUE;
1180 case TVI_CONTROL_VBI_GET_VBIPAGE:
1181 if (NULL == (page = vbi_getpage(priv_vbi)))
1182 return TVI_CONTROL_FALSE;
1183 *(void **) arg = (void *) page;
1184 return TVI_CONTROL_TRUE;
1185 case TVI_CONTROL_VBI_GET_TXTPAGE:
1186 if (NULL == (txtpage = vbi_getpagetext(priv_vbi)))
1187 return TVI_CONTROL_FALSE;
1188 *(void **) arg = (void *) txtpage;
1189 return TVI_CONTROL_TRUE;
1190 case TVI_CONTROL_VBI_GET_IMGPAGE:
1191 if (NULL == (img = vbi_getpageimg(priv_vbi)))
1192 return TVI_CONTROL_FALSE;
1193 *(void **) arg = (void *) img;
1194 return TVI_CONTROL_TRUE;
1195 }
1196 return TVI_CONTROL_UNKNOWN;
1197 }