lirc anc lircc are MPlayer-only, so add LDFLAGS for MPlayer link only.
[mplayer/glamo.git] / stream / tvi_vbi.c
blobc060f43ef2fd1e675610d98a8f2ab18d5ea6a791
1 /*
2 * Teletext support
4 * Copyright (C) 2007 Vladimir Voroshilov <voroshil@gmail.com>
6 * This file is part of MPlayer.
8 * MPlayer is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * MPlayer is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License along
19 * with MPlayer; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 * Based on Attila Otvos' teletext patch, Michael Niedermayer's
24 * proof-of-concept teletext capture utility and some parts
25 * (decode_raw_line_runin,pll_add,pll_reset) of MythTV project.
26 * Code for calculating [soc:eoc] is based on aletv of Edgar Toernig.
28 * Teletext system is described in
29 * ETS 300 706 "Enhanced Teletext specification" : May 1997
30 * http://www.themm.net/~mihu/linux/saa7146/specs/ets_300706e01p.pdf
32 * Some implementation details:
33 * How to port teletext to another tvi_* driver (see tvi_v4l2.c for example):
35 * 1. Implement TVI_CONTROL_VBI_INIT (initialize driver-related vbi subsystem,
36 * start grabbing thread)
37 * input data: vbi device name.
38 * (driver should also call TV_VBI_CONTROL_START for common vbi subsystem initialization
39 * with pointer to initialized tt_stream_properties structure.
40 * After ioctl call variable will contain pointer to initialized priv_vbi_t structure.
42 * 2. After receiving next chunk of raw vbi data call TV_VBI_CONTROL_DECODE_PAGE
43 * ioctl with pointer to data buffer
44 * 3. pass all other VBI related ioctl cmds to teletext_control routine
46 * Page displaying process consist of following stages:
48 * ---grabbing stage---
49 * 0. stream/tvi_*.c: vbi_grabber(...)
50 * getting vbi data from video device
51 * ---decoding stage---
52 * 1. stream/tvi_vbi.c: decode_raw_line_runin(...) or decode_raw_line_sine(...)
53 * decode raw vbi data into sliced 45(?) bytes long packets
54 * 2. stream/tvi_vbi.c: decode_pkt0(...), decode_pkt_page(...)
55 * packets processing (header analyzing, storing complete page in cache,
56 * only raw member of tt_char is filled at this stage)
57 * 3. stream/tvi_vbi.c: decode_page(...)
58 * page decoding. filling unicode,gfx,ctl,etc members of tt_char structure
59 * with appropriate values according to teletext control chars, converting
60 * text to utf8.
61 * ---rendering stage---
62 * 4. stream/tvi_vbi.c: prepare_visible_page(...)
63 * processing page. adding number of just received by background process
64 * teletext page, adding current time,etc.
65 * 5. libvo/sub.c: vo_update_text_teletext(...)
66 * rendering displayable osd with text and graphics
68 * TODO:
69 * v4lv1,bktr support
70 * spu rendering
71 * is better quality on poor signal possible ?
72 * link support
73 * font autoscale
74 * greyscale osd
75 * slave command for dumping pages
76 * fix bcd<->dec as suggested my Michael
78 * BUGS:
79 * wrong colors in debug dump
80 * blinking when visible page was just updated
83 #include "config.h"
85 #include <stdlib.h>
86 #include <string.h>
87 #include <unistd.h>
88 #include <errno.h>
89 #include <math.h>
90 #include <stdio.h>
92 #include <pthread.h>
94 #include "tv.h"
95 #include "mp_msg.h"
96 #include "help_mp.h"
97 #include "libmpcodecs/img_format.h"
98 #include "libavutil/common.h"
99 #include "input/input.h"
100 #include "osdep/timer.h"
102 //#define DEBUG_DUMP 1
104 /// page magazine entry structure
105 typedef struct mag_s{
106 tt_page* pt;
107 int order;
108 } mag_t;
110 typedef struct {
111 int on; ///< teletext on/off
112 int pagenum; ///< seek page number
113 int subpagenum; ///< seek subpage
114 int curr_pagenum; ///< current page number
115 int pagenumdec; ///< set page num with dec
117 teletext_format tformat; ///< see teletext_format enum
118 teletext_zoom zoom; ///< see teletext_zoom enum
119 mag_t* mag; ///< pages magazine (has 8 entities)
120 int primary_language; ///< primary character set
121 int secondary_language; ///< secondary character set
122 /// Currently displayed page (with additional info, e.g current time)
123 tt_char display_page[VBI_ROWS*VBI_COLUMNS];
124 /// number of raw bytes between two subsequent encoded bits
125 int bpb;
126 /// clock run-in sequence will be searched in buffer in [soc:eoc] bytes range
127 int soc;
128 int eoc;
129 /// minimum number of raw vbi bytes wich can be decoded into 8 data bits
130 int bp8bl;
131 /// maximum number of raw vbi bytes wich can be decoded into 8 data bits
132 int bp8bh;
134 int pll_adj;
135 int pll_dir;
136 int pll_cnt;
137 int pll_err;
138 int pll_lerr;
139 int pll_fixed;
140 /// vbi stream properties (buffer size,bytes per line, etc)
141 tt_stream_props* ptsp;
142 pthread_mutex_t buffer_mutex;
144 tt_page** ptt_cache;
145 unsigned char* ptt_cache_first_subpage;
146 /// network info
147 unsigned char initialpage;
148 unsigned int initialsubpage;
149 unsigned int networkid;
150 int timeoffset; // timeoffset=realoffset*2
151 unsigned int juliandate;
152 unsigned int universaltime;
153 unsigned char networkname[21];
154 int cache_reset;
155 /// "page changed" flag: 0-unchanged, 1-entire page, 3-only header
156 int page_changed;
157 int last_rendered;
158 } priv_vbi_t;
160 static unsigned char fixParity[256];
162 static const tt_char tt_space={0x20,7,0,0,0,0,0,0,0x20};
163 static const tt_char tt_error={'?',1,0,0,0,0,0,0,'?'}; // Red '?' on black background
164 static double si[12];
165 static double co[12];
167 #define VBI_FORMAT(priv) (*(priv->ptsp))
169 #define FIXP_SH 16
170 #define ONE_FIXP (1<<FIXP_SH)
171 #define FIXP2INT(a) ((a)>>FIXP_SH)
172 #define ANY2FIXP(a) ((int)((a)*ONE_FIXP))
174 static const unsigned char corrHamm48[256]={
175 0x01, 0xff, 0x01, 0x01, 0xff, 0x00, 0x01, 0xff,
176 0xff, 0x02, 0x01, 0xff, 0x0a, 0xff, 0xff, 0x07,
177 0xff, 0x00, 0x01, 0xff, 0x00, 0x00, 0xff, 0x00,
178 0x06, 0xff, 0xff, 0x0b, 0xff, 0x00, 0x03, 0xff,
179 0xff, 0x0c, 0x01, 0xff, 0x04, 0xff, 0xff, 0x07,
180 0x06, 0xff, 0xff, 0x07, 0xff, 0x07, 0x07, 0x07,
181 0x06, 0xff, 0xff, 0x05, 0xff, 0x00, 0x0d, 0xff,
182 0x06, 0x06, 0x06, 0xff, 0x06, 0xff, 0xff, 0x07,
183 0xff, 0x02, 0x01, 0xff, 0x04, 0xff, 0xff, 0x09,
184 0x02, 0x02, 0xff, 0x02, 0xff, 0x02, 0x03, 0xff,
185 0x08, 0xff, 0xff, 0x05, 0xff, 0x00, 0x03, 0xff,
186 0xff, 0x02, 0x03, 0xff, 0x03, 0xff, 0x03, 0x03,
187 0x04, 0xff, 0xff, 0x05, 0x04, 0x04, 0x04, 0xff,
188 0xff, 0x02, 0x0f, 0xff, 0x04, 0xff, 0xff, 0x07,
189 0xff, 0x05, 0x05, 0x05, 0x04, 0xff, 0xff, 0x05,
190 0x06, 0xff, 0xff, 0x05, 0xff, 0x0e, 0x03, 0xff,
191 0xff, 0x0c, 0x01, 0xff, 0x0a, 0xff, 0xff, 0x09,
192 0x0a, 0xff, 0xff, 0x0b, 0x0a, 0x0a, 0x0a, 0xff,
193 0x08, 0xff, 0xff, 0x0b, 0xff, 0x00, 0x0d, 0xff,
194 0xff, 0x0b, 0x0b, 0x0b, 0x0a, 0xff, 0xff, 0x0b,
195 0x0c, 0x0c, 0xff, 0x0c, 0xff, 0x0c, 0x0d, 0xff,
196 0xff, 0x0c, 0x0f, 0xff, 0x0a, 0xff, 0xff, 0x07,
197 0xff, 0x0c, 0x0d, 0xff, 0x0d, 0xff, 0x0d, 0x0d,
198 0x06, 0xff, 0xff, 0x0b, 0xff, 0x0e, 0x0d, 0xff,
199 0x08, 0xff, 0xff, 0x09, 0xff, 0x09, 0x09, 0x09,
200 0xff, 0x02, 0x0f, 0xff, 0x0a, 0xff, 0xff, 0x09,
201 0x08, 0x08, 0x08, 0xff, 0x08, 0xff, 0xff, 0x09,
202 0x08, 0xff, 0xff, 0x0b, 0xff, 0x0e, 0x03, 0xff,
203 0xff, 0x0c, 0x0f, 0xff, 0x04, 0xff, 0xff, 0x09,
204 0x0f, 0xff, 0x0f, 0x0f, 0xff, 0x0e, 0x0f, 0xff,
205 0x08, 0xff, 0xff, 0x05, 0xff, 0x0e, 0x0d, 0xff,
206 0xff, 0x0e, 0x0f, 0xff, 0x0e, 0x0e, 0xff, 0x0e };
209 enum {
210 LATIN=0,
211 CYRILLIC1,
212 CYRILLIC2,
213 CYRILLIC3,
214 GREEK,
215 LANGS
218 // conversion table for chars 0x20-0x7F (UTF8)
219 // TODO: add another languages
220 static const unsigned int lang_chars[LANGS][0x60]={
222 //Latin
223 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,
224 0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,
225 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,
226 0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,
227 0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,
228 0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,
229 0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,
230 0x58,0x59,0x5a,0x5b,0x5c,0x5d,0x5e,0x5f,
231 0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,
232 0x68,0x69,0x6a,0x6b,0x6c,0x6d,0x6e,0x6f,
233 0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,
234 0x78,0x79,0x7a,0x7b,0x7c,0x7d,0x7e,0x7f
237 //Cyrillic-1 (Serbian/Croatian)
238 0x20,0x21,0x22,0x23,0x24,0x25,0x044b,0x27,
239 0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,
240 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,
241 0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,
242 0x0427,0x0410,0x0411,0x0426,0x0414,0x0415,0x0424,0x0413,
243 0x0425,0x0418,0x0408,0x041a,0x041b,0x041c,0x041d,0x041e,
244 0x041f,0x040c,0x0420,0x0421,0x0422,0x0423,0x0412,0x0403,
245 0x0409,0x040a,0x0417,0x040b,0x0416,0x0402,0x0428,0x040f,
246 0x0447,0x0430,0x0431,0x0446,0x0434,0x0435,0x0444,0x0433,
247 0x0445,0x0438,0x0428,0x043a,0x043b,0x043c,0x043d,0x043e,
248 0x043f,0x042c,0x0440,0x0441,0x0442,0x0443,0x0432,0x0423,
249 0x0429,0x042a,0x0437,0x042b,0x0436,0x0422,0x0448,0x042f
252 //Cyrillic-2 (Russian/Bulgarian)
253 0x20,0x21,0x22,0x23,0x24,0x25,0x044b,0x27,
254 0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,
255 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,
256 0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,
257 0x042e,0x0410,0x0411,0x0426,0x0414,0x0415,0x0424,0x0413,
258 0x0425,0x0418,0x0419,0x041a,0x041b,0x041c,0x041d,0x041e,
259 0x041f,0x042f,0x0420,0x0421,0x0422,0x0423,0x0416,0x0412,
260 0x042c,0x042a,0x0417,0x0428,0x042d,0x0429,0x0427,0x042b,
261 0x044e,0x0430,0x0431,0x0446,0x0434,0x0435,0x0444,0x0433,
262 0x0445,0x0438,0x0439,0x043a,0x043b,0x043c,0x043d,0x043e,
263 0x043f,0x044f,0x0440,0x0441,0x0442,0x0443,0x0436,0x0432,
264 0x044c,0x044a,0x0437,0x0448,0x044d,0x0449,0x0447,0x044b
267 //Cyrillic-3 (Ukrainian)
268 0x20,0x21,0x22,0x23,0x24,0x25,0xef,0x27,
269 0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,
270 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,
271 0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,
272 0x042e,0x0410,0x0411,0x0426,0x0414,0x0415,0x0424,0x0413,
273 0x0425,0x0418,0x0419,0x041a,0x041b,0x041c,0x041d,0x041e,
274 0x041f,0x042f,0x0420,0x0421,0x0422,0x0423,0x0416,0x0412,
275 0x042c,0x49,0x0417,0x0428,0x042d,0x0429,0x0427,0xcf,
276 0x044e,0x0430,0x0431,0x0446,0x0434,0x0435,0x0444,0x0433,
277 0x0445,0x0438,0x0439,0x043a,0x043b,0x043c,0x043d,0x043e,
278 0x043f,0x044f,0x0440,0x0441,0x0442,0x0443,0x0436,0x0432,
279 0x044c,0x69,0x0437,0x0448,0x044d,0x0449,0x0447,0xFF
282 //Greek
283 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,
284 0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,
285 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,
286 0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,
287 0x0390,0x0391,0x0392,0x0393,0x0394,0x0395,0x0396,0x0397,
288 0x0398,0x0399,0x039a,0x039b,0x039c,0x039d,0x039e,0x039f,
289 0x03a0,0x03a1,0x03a2,0x03a3,0x03a4,0x03a5,0x03a6,0x03a7,
290 0x03a8,0x03a9,0x03aa,0x03ab,0x03ac,0x03ad,0x03ae,0x03af,
291 0x03b0,0x03b1,0x03b2,0x03b3,0x03b4,0x03b5,0x03b6,0x03b7,
292 0x03b8,0x03b9,0x03ba,0x03bb,0x03bc,0x03bd,0x03be,0x03bf,
293 0x03c0,0x03c1,0x03c2,0x03c3,0x03c4,0x03c5,0x03c6,0x03c7,
294 0x03c8,0x03c9,0x03ca,0x03cb,0x03cc,0x03cd,0x03ce,0x03cf
299 * Latin National Option Sub-Sets
300 * see Table 36 of ETS specification for details.
302 * 00: £ $ @ « ½ » ¬ # ­ ¼ ¦ ¾ ÷ English
303 * 01: é ï à ë ê ù î # è â ô û ç French
304 * 02: # ¤ É Ä Ö Å Ü _ é ä ö å ü Swedish/Finnish/Hungarian
305 * 03: # ů č ť ž ý í ř é á ě ú š Czech/Slovak
306 * 04: # $ § Ä Ö Ü ^ _ ° ä ö ü ß German
307 * 05: ç $ ¡ á é í ó ú ¿ ü ñ è à Portuguese/Spanish
308 * 06: £ $ é ° ç » ¬ # ù à ò è ì Italian
311 static const unsigned int latin_subchars[8][13]={
312 // English
313 {0xa3,0x24,0x40,0xab,0xbd,0xbb,0xac,0x23,0xad,0xbc,0xa6,0xbe,0xf7},
314 // French
315 {0xe9,0xef,0xe0,0xeb,0xea,0xf9,0xee,0x23,0xe8,0xe2,0xf4,0xfb,0xe7},
316 // Swedish/Finnish/Hungarian
317 {0x23,0xa4,0xc9,0xc4,0xd6,0xc5,0xdc,0x5f,0xe9,0xe4,0xf6,0xe5,0xfc},
318 // Czech/Slovak
319 {0x23,0x16f,0x10d,0x165,0x17e,0xfd,0xed,0x159,0xe9,0xe1,0x11b,0xfa,0x161},
320 // German
321 {0x23,0x24,0xa7,0xc4,0xd6,0xdc,0x5e,0x5f,0xb0,0xe4,0xf6,0xfc,0xdf},
322 // Portuguese/Spanish
323 {0xe7,0x24,0xa1,0xe1,0xe9,0xed,0xf3,0xfa,0xbf,0xfc,0xf1,0xe8,0xe0},
324 // Italian
325 {0xa3,0x24,0xe9,0xb0,0xe7,0xbb,0xac,0x23,0xf9,0xe0,0xf2,0xe8,0xec},
326 // Reserved
327 {0x23,0x24,0x40,0x5b,0x5c,0x5d,0x5e,0x5f,0x60,0x7b,0x7c,0x7d,0x7e}
331 * List of supported languages.
333 * lang_code bits for primary Language:
334 * bits 7-4 corresponds to bits 14-11 of 28 packet's first triplet
335 * bits 3-1 corresponds to bits C12-C14 of packet 0 (lang)
337 * lang_code bits for secondary Language:
338 * bits 7-5 corresponds to bits 3-1 of 28 packet's second triplet
339 * bits 4,2 corresponds to bits 18,16 of 28 packet's first triplet
340 * bits 3,1 corresponds to bits 15,17 of 28 packet's first triplet
342 * For details see Tables 32 and 33 of specification (subclause 15.2)
344 struct {
345 unsigned char lang_code;
346 unsigned char charset;
347 const char* lang_name;
348 } const tt_languages[]=
350 { 0x01, LATIN, "French"},
351 { 0x02, LATIN, "Swedish/Finnish/Hungarian"},
352 { 0x03, LATIN, "Czech/Slovak"},
353 { 0x04, LATIN, "German"},
354 { 0x05, LATIN, "Portuguese/Spanish"},
355 { 0x06, LATIN, "Italian"},
357 { 0x08, LATIN, "Polish"},
358 { 0x09, LATIN, "French"},
359 { 0x0a, LATIN, "Swedish/Finnish/Hungarian"},
360 { 0x0b, LATIN, "Czech/Slovak"},
361 { 0x0c, LATIN, "German"},
362 { 0x0e, LATIN, "Italian"},
364 { 0x10, LATIN, "English"},
365 { 0x11, LATIN, "French"},
366 { 0x12, LATIN, "Swedish/Finnish/Hungarian"},
367 { 0x13, LATIN, "Turkish"},
368 { 0x14, LATIN, "German"},
369 { 0x15, LATIN, "Portuguese/Spanish"},
370 { 0x16, LATIN, "Italian"},
372 { 0x1d, LATIN, "Serbian/Croatian/Slovenian (Latin)"},
374 { 0x20, CYRILLIC1, "Serbian/Croatian (Cyrillic)"},
375 { 0x21, CYRILLIC2, "Russian, Bulgarian"},
376 { 0x22, LATIN, "Estonian"},
377 { 0x23, LATIN, "Czech/Slovak"},
378 { 0x24, LATIN, "German"},
379 { 0x25, CYRILLIC3, "Ukrainian"},
380 { 0x26, LATIN, "Lettish/Lithuanian"},
382 { 0x33, LATIN, "Turkish"},
383 { 0x37, GREEK, "Greek"},
385 { 0x40, LATIN, "English"},
386 { 0x41, LATIN, "French"},
387 // { 0x47, ARABIC, "Arabic"},
389 // { 0x55, HEBREW, "Hebrew"},
390 // { 0x57, ARABIC, "Arabic"},
392 { 0x00, LATIN, "English"},
396 * \brief 24/18 Hamming code decoding
397 * \param data bytes with hamming code (array must be at least 3 bytes long)
398 * \return -1 if multiple bit error occured, D1-DI data bits - otherwise
400 * \note Bits must be correctly ordered, that is for 24/18 (lowest bit first)
401 * P1 P2 D1 P3 D2 D3 D4 P4 D5 D6 D7 D8 D9 DA DB P5 DC DD DE DF DG DH DI P6
403 static int corrHamm24(unsigned char *data){
404 unsigned char syndrom=0;
405 int cw=data[0] | (data[1]<<8) | (data[2]<<16);
406 int i;
408 for(i=0;i<23;i++)
409 syndrom^=((cw>>i)&1)*(i+33);
411 syndrom^=(cw>>11)&32;
413 if(syndrom&31){
414 if(syndrom < 32 || syndrom > 55)
415 return -1;
416 cw ^= 1<<((syndrom&31)-1);
419 return (cw&4)>>2 |
420 (cw&0x70)>>3 |
421 (cw&0x3f00)>>4 |
422 (cw&0x3f0000)>>5;
426 * \brief converts language bits to charset index
427 * \param lang language bits
428 * \return charset index in lang_chars array
430 static int lang2charset (int lang){
431 int i;
432 for(i=0;tt_languages[i].lang_code;i++)
433 if(tt_languages[i].lang_code==lang)
434 break;
436 return tt_languages[i].charset;
440 * \brief convert chars from curent teletext codepage into MPlayer charset
441 * \param p raw teletext char to decode
442 * \param charset index on lang_chars
443 * \param lang index in substitution array (latin charset only)
444 * \return UTF8 char
446 * \remarks
447 * routine will analyze raw member of given tt_char structure and
448 * fill unicode member of the same struct with appropriate utf8 code.
450 static unsigned int conv2uni(unsigned int p,int charset,int lang)
453 if(p<0x80 && p>=0x20){
454 if(charset==LATIN){
455 lang&=7;
456 if (p>=0x23 && p<=0x24){
457 return latin_subchars[lang][p-0x23];
458 }else if (p==0x40){
459 return latin_subchars[lang][2];
460 }else if (p>=0x5b && p<=0x60){
461 return latin_subchars[lang][p-0x5b+3];
462 }else if (p>=0x7b && p<=0x7e){
463 return latin_subchars[lang][p-0x7b+9];
466 return lang_chars[charset][p-0x20];
467 }else
468 return 0x20;
471 static void init_vbi_consts(priv_vbi_t* priv){
472 int i,j;
473 double ang;
474 for(i=0; i<256; i++){
475 j=i&0x7F;
476 j^= j+j;
477 j^= j<<2;
478 j^= j<<4;
479 fixParity[i]= i ^ (j&0x80) ^ 0x80;
482 for(i=0,ang=0; i<12; i++,ang+=M_PI/priv->bpb){
483 si[i]= sin(ang);
484 co[i]= cos(ang);
487 priv->bpb=(priv->ptsp->sampling_rate/6937500.0)*ONE_FIXP+0.5;
488 priv->soc=FFMAX(9.2e-6*priv->ptsp->sampling_rate-priv->ptsp->offset, 0);
489 priv->eoc=FFMIN(12.9e-6*priv->ptsp->sampling_rate-priv->ptsp->offset,
490 priv->ptsp->samples_per_line-43*8*priv->bpb/ONE_FIXP);
491 if (priv->eoc - priv->soc<16*priv->bpb/ONE_FIXP){ // invalid [soc:eoc]
492 priv->soc=0;
493 priv->eoc=92;
495 priv->bp8bl=0.97*8*priv->bpb/ONE_FIXP; // -3% tolerance
496 priv->bp8bh=1.03*8*priv->bpb/ONE_FIXP; // +3% tolerance
499 * \brief calculate increased/decreased by given value page number
500 * \param curr current page number in hexadecimal for
501 * \param direction decimal value (can be negative) to add to value
502 * of curr parameter
503 * \return new page number in hexadecimal form
505 * VBI page numbers are represented in special hexadecimal form, e.g.
506 * page with number 123 (as seen by user) internally has number 0x123.
507 * and equation 0x123+8 should be equal to 0x131 instead of regular 0x12b.
510 * Page numbers 0xYYY (where Y is not belongs to (0..9).
511 * Page number belongs to [0x000,0x799] or [0x100:0x899] (first 0 can be
512 * treated as '8')
514 static int steppage(int p, int direction, int skip_hidden)
516 if(skip_hidden)
517 p=(p&15)+((p>>4)&15)*10+(p>>8)*100;
518 p+=direction;
519 if(skip_hidden){
520 p=(p+800)%800;
521 p=(p%10)+((p/10)%10)*16+(p/100)*256;
524 return p&0x7ff;
528 ------------------------------------------------------------------
529 Cache stuff
530 ------------------------------------------------------------------
534 * \brief add/update entry in cache
535 * \param priv private data structure
536 * \param pg page to store in cache
537 * \param line line to update (value below 0 means update entire page)
539 static void put_to_cache(priv_vbi_t* priv,tt_page* pg,int line){
540 tt_page* pgc; //page in cache
541 int i,j,count;
543 if(line<0){
544 i=0;
545 count=VBI_ROWS*VBI_COLUMNS;
546 }else if(line<VBI_ROWS){
547 i=line*VBI_COLUMNS;
548 count=(line+1)*VBI_COLUMNS;
549 }else
550 return;
552 pthread_mutex_lock(&(priv->buffer_mutex));
554 if(!priv->ptt_cache[pg->pagenum]){
555 priv->ptt_cache[pg->pagenum]=calloc(1,sizeof(tt_page));
556 pgc=priv->ptt_cache[pg->pagenum];
557 }else{
558 pgc=priv->ptt_cache[pg->pagenum];
559 while(pgc->next_subpage && pgc->subpagenum!=pg->subpagenum)
560 pgc=pgc->next_subpage;
562 if(pgc->subpagenum!=pg->subpagenum){
563 pgc->next_subpage=calloc(1,sizeof(tt_page));
564 pgc=pgc->next_subpage;
567 pgc->pagenum=pg->pagenum;
568 pgc->subpagenum=pg->subpagenum;
569 pgc->primary_lang=pg->primary_lang;
570 pgc->secondary_lang=pg->secondary_lang;
571 pgc->flags=pg->flags;
572 for(j=0;j<6;++j)
573 pgc->links[j]=pg->links[j];
574 //instead of copying entire page into cache, copy only undamaged
575 //symbols into cache
576 for(;i<count;i++){
577 if(!(pg->raw[i]&0x80))
578 pgc->raw[i]=pg->raw[i];
579 else
580 mp_msg(MSGT_TV,MSGL_DBG3,"char error. pg:%x, c[%d]=0x%x\n",
581 pg->pagenum,i,pg->raw[i]);
583 pgc->active=1;
584 pthread_mutex_unlock(&(priv->buffer_mutex));
588 * \brief get any subpage number of given page
589 * \param priv private data structure
590 * \param pagenum page number to search subpages in
592 * \return subpage number of first found subpage which belongs to
593 * given page number
595 * \note page itself is subpage too (and usually has subpage number 0)
597 static inline int get_subpagenum_from_cache(priv_vbi_t* priv, int pagenum){
598 if (!priv->ptt_cache[pagenum])
599 return 0x3f7f;
600 else
601 return priv->ptt_cache[pagenum]->subpagenum;
605 * \brief get page from cache by it page and subpage number
606 * \param priv private data structure
607 * \param pagenum page number
608 * \param subpagenum subpage number
610 * \return pointer to tt_page structure if requested page is found
611 * and NULL otherwise
613 static inline tt_page* get_from_cache(priv_vbi_t* priv, int pagenum,int subpagenum){
614 tt_page* tp=priv->ptt_cache[pagenum];
616 while(tp && tp->subpagenum!=subpagenum)
617 tp=tp->next_subpage;
618 return tp;
622 * \brief clears cache
623 * \param priv private data structure
625 * Deletes all tt_page structures from cache and frees allocated memory.
626 * Only zero-filled array of pointers remains in memory
628 static void clear_cache(priv_vbi_t* priv){
629 int i;
630 tt_page* tp;
633 Skip next 5 buffers to avoid mixing teletext pages from different
634 channels during channel switch
636 priv->cache_reset=5;
637 for(i=0;i<VBI_MAX_PAGES;i++){
638 while(priv->ptt_cache[i]){
639 tp=priv->ptt_cache[i];
640 priv->ptt_cache[i]=tp->next_subpage;
641 free(tp);
644 priv->initialsubpage=priv->networkid=0;
645 priv->timeoffset=0;
646 priv->juliandate=priv->universaltime=0;
647 memset(priv->networkname,0,21);
651 * \brief cache initialization
652 * \param priv private data structure
654 * \note Has to be called before any cache operations!
656 static void init_cache(priv_vbi_t* priv){
657 priv->ptt_cache=calloc(VBI_MAX_PAGES,sizeof(tt_page*));
661 * \brief destroys cache
662 * \param priv private data structure
664 * Frees all memory allocated for cache (including array of pointers).
665 * It is safe to call this routine multiple times
667 static void destroy_cache(priv_vbi_t* priv){
668 if(priv->ptt_cache){
669 clear_cache(priv);
670 free(priv->ptt_cache);
671 priv->ptt_cache=NULL;
676 ------------------------------------------------------------------
677 Decoder stuff
678 ------------------------------------------------------------------
681 * \brief converts raw teletext page into useful format (1st rendering stage)
682 * \param pg page to decode
683 * \param raw raw data to decode page from
684 * \param primary_lang primary language code
685 * \param secondary_lang secondary language code
687 * Routine fills tt_char structure of each teletext_page character with proper
688 * info about foreground and background colors, character
689 * type (graphics/control/text).
691 static void decode_page(tt_char* p,unsigned char* raw,int primary_lang,int secondary_lang,int flags)
693 int row,col;
694 int prim_charset=lang2charset(primary_lang);
695 int sec_charset=lang2charset(secondary_lang);
697 for(row=0;row<VBI_ROWS;row++) {
698 int prim_lang=1;
699 int gfx=0;
700 int fg_color=7;
701 int bg_color=0;
702 int separated=0;
703 int conceal=0;
704 int hold=0;
705 int flash=0;
706 int box=0;
708 tt_char tt_held=tt_space;
709 for(col=0;col<VBI_COLUMNS;col++){
710 int i=row*VBI_COLUMNS+col;
711 int c=raw[i];
712 p[i].raw=c;
713 if(c&0x80){ //damaged char
714 p[i]=tt_error;
715 continue;
717 if((flags&TT_PGFL_SUBTITLE) || (flags&TT_PGFL_NEWFLASH))
718 p[i].hidden=!box;
719 else
720 p[i].hidden=0;
721 p[i].gfx=gfx?(separated?2:1):0;
722 p[i].lng=prim_lang;
723 p[i].ctl=(c&0x60)==0?1:0;
724 p[i].fg=fg_color;
725 p[i].bg=bg_color;
726 p[i].flh=flash;
728 if ((c&0x60)==0){ //control chars
729 if(c>=0x08 && c<=0x09){//Flash/Steady
730 flash=c==0x08;
731 p[i].flh=flash;
732 if(c==0x09){
733 p[i].fg=fg_color;
734 p[i].bg=bg_color;
736 }else if(c>=0x0a && c<=0x0b){
737 box=c&1;
738 }else if(c>=0x0c && c<=0x0f){
739 }else if (c<=0x17){ //colors
740 fg_color=c&0x0f;
741 gfx=c>>4;
742 conceal=0;
743 if(!gfx) hold=0;
744 }else if (c<=0x18){
745 conceal=1;
746 }else if (c<=0x1a){ //Contiguous/Separated gfx
747 separated=!(c&1);
748 }else if (c<=0x1b){
749 prim_lang=!prim_lang;
750 }else if (c<=0x1d){
751 bg_color=(c&1)?fg_color:0;
752 p[i].bg=bg_color;
753 }else{ //Hold/Release Graphics
754 hold=!(c&1);
756 p[i].ctl=1;
757 if(hold || c==0x1f){
758 p[i]=tt_held;
759 p[i].fg=fg_color;
760 p[i].bg=bg_color;
761 }else
762 p[i].unicode=p[i].gfx?0:' ';
763 continue;
766 if(conceal){
767 p[i].gfx=0;
768 p[i].unicode=' ';
769 }else if(gfx){
770 p[i].unicode=c-0x20;
771 if (p[i].unicode>0x3f) p[i].unicode-=0x20;
772 tt_held=p[i];
773 }else{
774 if(p[i].lng){
775 p[i].unicode=conv2uni(c,prim_charset,primary_lang&7);
776 }else{
777 p[i].unicode=conv2uni(c,sec_charset,secondary_lang&7);
780 p[i].fg=fg_color;
781 p[i].bg=bg_color;
787 * \brief prepares current page for displaying
788 * \param priv_vbi private data structure
790 * Routine adds some useful info (time and page number of page, grabbed by
791 * background thread to top line of current page). Displays "No teletext"
792 * string if no vbi data available.
794 #define PRINT_HEX(dp,i,h) dp[i].unicode=((h)&0xf)>9?'A'+((h)&0xf)-10:'0'+((h)&0xf)
795 static void prepare_visible_page(priv_vbi_t* priv){
796 tt_page *pg,*curr_pg;
797 unsigned char *p;
798 int i;
800 pthread_mutex_lock(&(priv->buffer_mutex));
801 mp_msg(MSGT_TV,MSGL_DBG3,"tvi_vbi: prepare_visible_page pg:0x%x, sub:0x%x\n",
802 priv->pagenum,priv->subpagenum);
803 if(priv->subpagenum==0x3f7f) //no page yet
804 priv->subpagenum=get_subpagenum_from_cache(priv,priv->pagenum);
806 pg=get_from_cache(priv,priv->pagenum,priv->subpagenum);
807 mp_dbg(MSGT_TV,MSGL_DBG3,"tvi_vbi: prepare_vibible_page2 pg:0x%x, sub:0x%x\n",
808 priv->pagenum,priv->subpagenum);
810 curr_pg=get_from_cache(priv,priv->curr_pagenum,
811 get_subpagenum_from_cache(priv,priv->curr_pagenum));
812 if (!pg && !curr_pg){
813 p=MSGTR_TV_NoTeletext;
814 for(i=0;i<VBI_COLUMNS && *p;i++){
815 GET_UTF8(priv->display_page[i].unicode,*p++,break;);
817 for(;i<VBI_ROWS*VBI_COLUMNS;i++)
818 priv->display_page[i]=tt_space;
819 pthread_mutex_unlock(&(priv->buffer_mutex));
820 return;
823 if (!pg || !pg->active){
824 for(i=0;i<VBI_ROWS*VBI_COLUMNS;i++){
825 priv->display_page[i]=tt_space;
827 }else{
828 decode_page(priv->display_page,pg->raw,pg->primary_lang,pg->secondary_lang,pg->flags);
829 mp_msg(MSGT_TV,MSGL_DBG3,"page #%x was decoded!\n",pg->pagenum);
832 PRINT_HEX(priv->display_page,0,(priv->curr_pagenum&0x700)?priv->curr_pagenum>>8:8);
833 PRINT_HEX(priv->display_page,1,priv->curr_pagenum>>4);
834 PRINT_HEX(priv->display_page,2,priv->curr_pagenum);
835 priv->display_page[3].unicode=' ';
836 priv->display_page[4].unicode=' ';
837 switch(priv->pagenumdec>>12){
838 case 1:
839 priv->display_page[5].unicode='_';
840 priv->display_page[6].unicode='_';
841 PRINT_HEX(priv->display_page,7,priv->pagenumdec);
842 break;
843 case 2:
844 priv->display_page[5].unicode='_';
845 PRINT_HEX(priv->display_page,6,priv->pagenumdec>>4);
846 PRINT_HEX(priv->display_page,7,priv->pagenumdec);
847 break;
848 default:
849 PRINT_HEX(priv->display_page,5,(priv->pagenum&0x700)?priv->pagenum>>8:8);
850 PRINT_HEX(priv->display_page,6,priv->pagenum>>4);
851 PRINT_HEX(priv->display_page,7,priv->pagenum);
853 if(priv->subpagenum!=0x3f7f){
854 priv->display_page[8].unicode='.';
855 PRINT_HEX(priv->display_page,9,priv->subpagenum>>4);
856 PRINT_HEX(priv->display_page,10,priv->subpagenum);
857 }else{
858 priv->display_page[8].unicode=' ';
859 priv->display_page[9].unicode=' ';
860 priv->display_page[10].unicode=' ';
862 priv->display_page[11].unicode=' ';
863 for(i=VBI_COLUMNS;i>VBI_TIME_LINEPOS ||
864 ((curr_pg->raw[i]&0x60) && curr_pg->raw[i]!=0x20 && i>11);
865 --i)
866 if(curr_pg->raw[i]&0x60)
867 priv->display_page[i].unicode=curr_pg->raw[i];
868 else
869 priv->display_page[i].unicode=' ';
870 pthread_mutex_unlock(&(priv->buffer_mutex));
873 ------------------------------------------------------------------
874 Renderer stuff
875 ------------------------------------------------------------------
877 #ifdef DEBUG_DUMP
879 * \brief renders teletext page into given file
880 * \param pt page to render
881 * \param f opened file descriptor
882 * \param pagenum which page to render
883 * \param colored use colors not implementede yet)
885 * Text will be UTF8 encoded
887 static void render2text(tt_page* pt,FILE* f,int colored){
888 int i,j;
889 unsigned int u;
890 unsigned char buf[8];
891 unsigned char tmp;
892 int pos;
893 tt_char dp[VBI_ROWS*VBI_COLUMNS];
894 int color=0;
895 int bkg=0;
896 int c1,b1;
897 if(!pt)
898 return;
899 fprintf(f,"+========================================+\n");
900 fprintf(f,"| lang:%d pagenum:0x%x subpagenum:%d flags:0x%x|\n",
901 pt->lang,
902 pt->pagenum,
903 pt->subpagenum,
905 fprintf(f,"+----------------------------------------+\n");
907 decode_page(dp,pt->raw,pt->primary_lang,pt->secondary_lang,pt->flags);
908 for(i=0;i<VBI_ROWS;i++){
909 fprintf(f,"|");
910 if(colored) fprintf(f,"\033[40m");
911 for(j=0;j<VBI_COLUMNS;j++)
913 u=dp[i*VBI_COLUMNS+j].unicode;
914 if(dp[i*VBI_COLUMNS+j].fg <= 7)
915 c1=30+dp[i*VBI_COLUMNS+j].fg;
916 else
917 c1=38;
918 if(dp[i*VBI_COLUMNS+j].bg <= 7)
919 b1=40+dp[i*VBI_COLUMNS+j].bg;
920 else
921 b1=40;
922 if (b1!=bkg && colored){
923 fprintf(f,"\033[%dm",b1);
924 bkg=b1;
926 if(c1!=color && colored){
927 fprintf(f,"\033[%dm",c1);
928 color=c1;
930 if(dp[i*VBI_COLUMNS+j].gfx){
931 fprintf(f,"*");
932 }else{
933 pos=0;
934 PUT_UTF8(u,tmp,if(pos<7) buf[pos++]=tmp;);
935 buf[pos]='\0';
936 fprintf(f,"%s",buf);
940 if (colored) fprintf(f,"\033[0m");
941 color=-1;bkg=-1;
942 fprintf(f,"|\n");
944 #if 1
945 //for debug
946 fprintf(f,"+====================raw=================+\n");
947 for(i=0;i<VBI_ROWS;i++){
948 for(j=0;j<VBI_COLUMNS;j++)
949 fprintf(f,"%02x ",dp[i*VBI_COLUMNS+j].raw);
950 fprintf(f,"\n");
952 fprintf(f,"+====================lng=================+\n");
953 for(i=0;i<VBI_ROWS;i++){
954 for(j=0;j<VBI_COLUMNS;j++)
955 fprintf(f,"%02x ",dp[i*VBI_COLUMNS+j].lng);
956 fprintf(f,"\n");
958 #endif
959 fprintf(f,"+========================================+\n");
963 * \brief dump page into pgXXX.txt file in vurrent directory
964 * \param pt page to dump
966 * \note XXX in filename is page number
967 * \note use only for debug purposes
969 static void dump_page(tt_page* pt)
971 FILE*f;
972 char name[100];
973 snprintf(name,99,"pg%x.txt",pt->pagenum);
974 f=fopen(name,"wb");
975 render2text(pt,f,1);
976 fclose(f);
978 #endif //DEBUG_DUMP
982 * \brief checks whether page is ready and copies it into cache array if so
983 * \param priv private data structure
984 * \param magAddr page's magazine address (0-7)
986 * Routine also calls decode_page to perform 1st stage of rendering
988 static void store_in_cache(priv_vbi_t* priv, int magAddr, int line){
989 mp_msg(MSGT_TV,MSGL_DBG2,"store_in_cache(%d): pagenum:%x\n",
990 priv->mag[magAddr].order,
991 priv->mag[magAddr].pt->pagenum);
993 put_to_cache(priv,priv->mag[magAddr].pt,line);
994 priv->curr_pagenum=priv->mag[magAddr].pt->pagenum;
996 #ifdef DEBUG_DUMP
997 dump_page(get_from_cache(priv,
998 priv->mag[magAddr].pt->pagenum,
999 priv->mag[magAddr].pt->subpagenum));
1000 #endif
1005 ------------------------------------------------------------------
1006 Grabber stuff
1007 ------------------------------------------------------------------
1009 #define PLL_SAMPLES 4
1010 #define PLL_ERROR 4
1011 #define PLL_ADJUST 4
1014 * \brief adjust current phase for better signal decoding
1015 * \param n count of bytes processed (?)
1016 * \param err count of error bytes (?)
1018 * \remarks code was got from MythTV project
1020 static void pll_add(priv_vbi_t* priv,int n,int err){
1021 if(priv->pll_fixed)
1022 return;
1023 if(err>PLL_ERROR*2/3)
1024 err=PLL_ERROR*2/3;
1025 priv->pll_err+=err;
1026 priv->pll_cnt+=n;
1027 if(priv->pll_cnt<PLL_SAMPLES)
1028 return;
1029 if(priv->pll_err>PLL_ERROR)
1031 if(priv->pll_err>priv->pll_lerr)
1032 priv->pll_dir= -priv->pll_dir;
1033 priv->pll_lerr=priv->pll_err;
1034 priv->pll_adj+=priv->pll_dir;
1035 if (priv->pll_adj<-PLL_ADJUST || priv->pll_adj>PLL_ADJUST)
1037 priv->pll_adj=0;
1038 priv->pll_dir=-1;
1039 priv->pll_lerr=0;
1041 mp_msg(MSGT_TV,MSGL_DBG3,"vbi: pll_adj=%2d\n",priv->pll_adj);
1043 priv->pll_cnt=0;
1044 priv->pll_err=0;
1048 * \brief reset error correction
1049 * \param priv private data structure
1050 * \param fine_tune shift value for adjusting
1052 * \remarks code was got from MythTV project
1054 static void pll_reset(priv_vbi_t* priv,int fine_tune){
1055 priv->pll_fixed=fine_tune >= -PLL_ADJUST && fine_tune <= PLL_ADJUST;
1057 priv->pll_err=0;
1058 priv->pll_lerr=0;
1059 priv->pll_cnt=0;
1060 priv->pll_dir=-1;
1061 priv->pll_adj=0;
1062 if(priv->pll_fixed)
1063 priv->pll_adj=fine_tune;
1064 if(priv->pll_fixed)
1065 mp_msg(MSGT_TV,MSGL_DBG3,"pll_reset (fixed@%2d)\n",priv->pll_adj);
1066 else
1067 mp_msg(MSGT_TV,MSGL_DBG3,"pll_reset (auto)\n");
1071 * \brief decode packet 0 (teletext page header)
1072 * \param priv private data structure
1073 * \param data raw teletext data (with not applied hamm correction yet)
1074 * \param magAddr teletext page's magazine address
1076 * \remarks
1077 * data buffer was shifted by 6 and now contains:
1078 * 0..1 page number
1079 * 2..5 sub-code
1080 * 6..7 control codes
1081 * 8..39 display data
1083 * only first 8 bytes protected by Hamm 8/4 code
1085 static int decode_pkt0(priv_vbi_t* priv,unsigned char* data,int magAddr)
1087 int d[8];
1088 int i,err;
1090 if (magAddr<0 || magAddr>7)
1091 return 0;
1092 for(i=0;i<8;i++){
1093 d[i]= corrHamm48[ data[i] ];
1094 if(d[i]&0x80){
1095 pll_add(priv,2,4);
1097 if(priv->mag[magAddr].pt)
1098 free(priv->mag[magAddr].pt);
1099 priv->mag[magAddr].pt=NULL;
1100 priv->mag[magAddr].order=0;
1101 return 0;
1104 if (!priv->mag[magAddr].pt)
1105 priv->mag[magAddr].pt= malloc(sizeof(tt_page));
1107 if(priv->primary_language)
1108 priv->mag[magAddr].pt->primary_lang=priv->primary_language;
1109 else
1110 priv->mag[magAddr].pt->primary_lang= (d[7]&7)>>1;
1111 priv->mag[magAddr].pt->secondary_lang=priv->secondary_language;
1112 priv->mag[magAddr].pt->subpagenum=(d[2]|(d[3]<<4)|(d[4]<<8)|(d[5]<<12))&0x3f7f;
1113 priv->mag[magAddr].pt->pagenum=(magAddr<<8) | d[0] | (d[1]<<4);
1114 priv->mag[magAddr].pt->flags=((d[7]&1)<<7) | ((d[3]&8)<<3) | ((d[5]&12)<<2) | d[6];
1116 memset(priv->mag[magAddr].pt->raw, 0x00, VBI_COLUMNS*VBI_ROWS);
1117 priv->mag[magAddr].order=0;
1119 for(i=0;i<8;i++){
1120 priv->mag[magAddr].pt->raw[i]=0x20;
1122 err=0;
1123 for(i=8; i<VBI_COLUMNS; i++){
1124 data[i]= fixParity[data[i]];
1125 priv->mag[magAddr].pt->raw[i]=data[i];
1126 if(data[i]&0x80) //Error
1127 err++;
1128 pll_add(priv,1,err);
1131 store_in_cache(priv,magAddr,0);
1133 return 1;
1137 * \brief decode teletext 8/30 Format 1 packet
1138 * \param priv private data structure
1139 * \param data raw teletext data (with not applied hamm correction yet)
1140 * \param magAddr teletext page's magazine address
1142 * \remarks
1143 * packet contains:
1144 * 0 designation code
1145 * 1..2 initial page
1146 * 3..6 initial subpage & magazine address
1147 * 7..8 network id
1148 * 9 time offset
1149 * 10..12 julian date
1150 * 13..15 universal time
1151 * 20..40 network name
1153 * First 7 bytes are protected by Hamm 8/4 code.
1154 * Bytes 20-40 has odd parity check.
1156 * See subcaluse 9.8.1 of specification for details
1158 static int decode_pkt30(priv_vbi_t* priv,unsigned char* data,int magAddr)
1160 int d[8];
1161 int i,err;
1163 for(i=0;i<7;i++){
1164 d[i]= corrHamm48[ data[i] ];
1165 if(d[i]&0x80){
1166 pll_add(priv,2,4);
1167 return 0;
1169 d[i]&=0xf;
1172 err=0;
1173 for(i=20; i<40; i++){
1174 data[i]= fixParity[data[i]];
1175 if(data[i]&0x80)//Unrecoverable error
1176 err++;
1177 pll_add(priv,1,err);
1179 if (err) return 0;
1181 if (d[0]&0xe) //This is not 8/30 Format 1 packet
1182 return 1;
1184 priv->initialpage=d[1] | d[2]<<4 | (d[6]&0xc)<<7 | (d[4]&1)<<8;
1185 priv->initialsubpage=d[3] | d[4]<<4 | d[5]<<8 | d[6]<<12;
1186 priv->networkid=data[7]<<8 | data[8];
1188 priv->timeoffset=(data[9]>>1)&0xf;
1189 if(data[9]&0x40)
1190 priv->timeoffset=-priv->timeoffset;
1192 priv->juliandate=(data[10]&0xf)<<16 | data[11]<<8 | data[12];
1193 priv->juliandate-=0x11111;
1195 priv->universaltime=data[13]<<16 | data[14]<<8 | data[15];
1196 priv->universaltime-=0x111111;
1198 snprintf(priv->networkname,21,"%s",data+20);
1200 return 1;
1204 * \brief decode packets 1..24 (teletext page header)
1205 * \param priv private data structure
1206 * \param data raw teletext data
1207 * \param magAddr teletext page's magazine address
1208 * \param rowAddr teletext page's row number
1210 * \remarks
1211 * data buffer was shifted by 6 and now contains 40 bytes of display data:
1212 * this type of packet is not proptected by Hamm 8/4 code
1214 static void decode_pkt_page(priv_vbi_t* priv,unsigned char*data,int magAddr,int rowAddr){
1215 int i,err;
1216 if (!priv->mag[magAddr].pt)
1217 return;
1219 priv->mag[magAddr].order=rowAddr;
1221 err=0;
1222 for(i=0; i<VBI_COLUMNS; i++){
1223 data[i]= fixParity[ data[i] ];
1224 priv->mag[magAddr].pt->raw[i+rowAddr*VBI_COLUMNS]=data[i];
1225 if( data[i]&0x80) //HammError
1226 err++;
1228 pll_add(priv,1,err);
1230 store_in_cache(priv,magAddr,rowAddr);
1234 * \brief decode packets 27 (teletext links)
1235 * \param priv private data structure
1236 * \param data raw teletext data
1237 * \param magAddr teletext page's magazine address
1239 static int decode_pkt27(priv_vbi_t* priv,unsigned char* data,int magAddr){
1240 int i,hpg;
1242 if (!priv->mag[magAddr].pt)
1243 return 0;
1244 for(i=0;i<38;++i)
1245 if ((data[i] = corrHamm48[ data[i] ]) & 0x80){
1246 pll_add(priv,2,4);
1247 return 0;
1251 Not a X/27/0 Format 1 packet or
1252 flag "show links on row 24" is not set.
1254 if (data[0] || !(data[37] & 8))
1255 return 1;
1256 for(i=0;i<6;++i) {
1257 hpg = (magAddr<<8) ^ ((data[4+i*6]&0x8)<<5 | (data[6+i*6]&0xc)<<7);
1258 if (!hpg) hpg=0x800;
1259 priv->mag[magAddr].pt->links[i].pagenum = (data[1+i*6] & 0xf) |
1260 ((data[2+i*6] & 0xf) << 4) | hpg;
1261 priv->mag[magAddr].pt->links[i].subpagenum = ((data[3+i*6] & 0xf) |
1262 (data[4+i*6] & 0xf) << 4 | (data[5+i*6] & 0xf) << 8 |
1263 (data[6+i*6] & 0xf) << 12) & 0x3f7f;
1265 put_to_cache(priv,priv->mag[magAddr].pt,-1);
1266 return 1;
1270 * \brief Decode teletext X/28/0 Format 1 packet
1271 * \param priv private data structure
1272 * \param data raw teletext data
1274 * Primary G0 charset is transmitted in bits 14-8 of Triplet 1
1275 * See Table 32 of specification for details.
1277 * Secondary G0 charset is transmitted in bits 3-1 of Triplet 2 and
1278 * bits 18-15 of Triplet 1
1279 * See Table 33 of specification for details.
1282 static void decode_pkt28(priv_vbi_t* priv,unsigned char*data){
1283 int d;
1284 int t1,t2;
1285 d=corrHamm48[ data[0] ];
1286 if(d) return; //this is not X/28/0 Format 1 packet or error occured
1288 t1=corrHamm24(data+1);
1289 t2=corrHamm24(data+4);
1290 if (t1<0 || t2<0){
1291 pll_add(priv,1,4);
1292 return;
1295 priv->primary_language=(t1>>7)&0x7f;
1296 priv->secondary_language=((t2<<4) | (t1>>14))&0x7f;
1297 if (priv->secondary_language==0x7f)
1298 //No secondary language required
1299 priv->secondary_language=priv->primary_language;
1300 else // Swapping bits 1 and 3
1301 priv->secondary_language=(priv->secondary_language&0x7a) |
1302 (priv->secondary_language&4)>>2 |
1303 (priv->secondary_language&1)<<2;
1305 mp_msg(MSGT_TV,MSGL_DBG2,"pkt28: language: primary=%02x secondary=0x%02x\n",
1306 priv->primary_language,priv->secondary_language);
1310 * \brief decodes raw vbi data (signal amplitudes) into sequence of bytes
1311 * \param priv private data structure
1312 * \param buf raw vbi data (one line of frame)
1313 * \param data output buffer for decoded bytes (at least 45 bytes long)
1315 * Used XawTV's algorithm. Signal phase is calculated with help of starting clock
1316 * run-in sequence (min/max values and bit distance values are calculated)
1318 static int decode_raw_line_runin(priv_vbi_t* priv,unsigned char* buf,unsigned char* data){
1319 const int magic= 0x27; // reversed 1110010
1320 int dt[256],hi[6],lo[6];
1321 int i,x,r;
1322 int decoded;
1323 int sync;
1324 unsigned char min,max;
1325 int thr=0; //threshold
1327 //stubs
1328 int soc=priv->soc;
1329 int eoc=priv->eoc;
1331 for(i=soc;i<eoc;i++)
1332 dt[i]=buf[i+priv->bpb/ONE_FIXP]-buf[i]; // amplifies the edges best.
1333 /* set barrier */
1334 for (i=eoc; i<eoc+16; i+=2)
1335 dt[i]=100, dt[i+1]=-100;
1337 /* find 6 rising and falling edges */
1338 for (i=soc, x=0; x<6; ++x)
1340 while (dt[i]<32)
1341 i++;
1342 hi[x]=i;
1343 while (dt[i]>-32)
1344 i++;
1345 lo[x]=i;
1347 if (i>=eoc)
1349 return 0; // not enough periods found
1351 i=hi[5]-hi[1]; // length of 4 periods (8 bits)
1352 if (i<priv->bp8bl || i>priv->bp8bh)
1354 mp_msg(MSGT_TV,MSGL_DBG3,"vbi: wrong freq %d (%d,%d)\n",
1355 i,priv->bp8bl,priv->bp8bh);
1356 return 0; // bad frequency
1358 /* AGC and sync-reference */
1359 min=255, max=0, sync=0;
1360 for (i=hi[4]; i<hi[5]; ++i)
1361 if (buf[i]>max)
1362 max=buf[i], sync=i;
1363 for (i=lo[4]; i<lo[5]; ++i)
1364 if (buf[i]<min)
1365 min=buf[i];
1366 thr=(min+max)/2;
1368 buf+=sync;
1369 // searching for '11'
1370 for(i=priv->pll_adj*priv->bpb/10;i<16*priv->bpb;i+=priv->bpb)
1371 if(buf[FIXP2INT(i)]>thr && buf[FIXP2INT(i+priv->bpb)]>thr)
1372 break;
1373 r=0;
1374 for(decoded=1; decoded<= (VBI_COLUMNS+3)<<3;decoded++){
1375 r>>=1;
1376 if(buf[FIXP2INT(i)]>thr) r|=0x80;
1377 if(!(decoded & 0x07)){
1378 data[(decoded>>3) - 1]=r;
1379 r=0;
1381 i+=priv->bpb;
1383 if(data[0]!=magic)
1384 return 0; //magic not found
1386 //stub
1387 for(i=0;i<43;i++){
1388 data[i]=data[i+1];
1390 mp_msg(MSGT_TV,MSGL_DBG3,"thr:%d sync:%d ",thr,sync);
1392 return 1;
1395 #if 0
1396 //See comment in vbi_decode for a reason of commenting out this routine.
1399 * \brief decodes raw vbi data (signal amplitudes) into sequence of bytes
1400 * \param priv private data structure
1401 * \param buf raw vbi data (one line of frame)
1402 * \param data output buffer for decoded bytes (at least 45 bytes long)
1404 * Used Michael Niedermayer's algorithm.
1405 * Signal phase is calculated using correlation between given samples data and
1406 * pure sine
1408 static int decode_raw_line_sine(priv_vbi_t* priv,unsigned char* buf,unsigned char* data){
1409 int i,x,r,amp,xFixp;
1410 int avg=0;
1411 double sin_sum=0, cos_sum=0;
1413 for(x=0; x< FIXP2INT(10*priv->bpb); x++)
1414 avg+=buf[x];
1416 avg/=FIXP2INT(10*priv->bpb);
1418 for(x=0; x<12; x++){
1419 amp= buf[x<<1];
1420 sin_sum+= si[x]*(amp-avg);
1421 cos_sum+= co[x]*(amp-avg);
1423 //this is always zero. Why ?
1424 xFixp= atan(sin_sum/cos_sum)*priv->bpb/M_PI;
1426 //Without this line the result is full of errors
1427 //and routine is unable to find magic sequence
1428 buf+=FIXP2INT(10*priv->bpb);
1430 r=0;
1431 for(x=FIXP2INT(xFixp);x<70;x=FIXP2INT(xFixp)){
1432 r=(r<<1) & 0xFFFF;
1433 if(buf[x]>avg) r|=1;
1434 xFixp+=priv->bpb;
1435 if(r==0xAAE4) break;
1438 //this is not teletext
1439 if (r!=0xaae4) return 0;
1441 //Decode remaining 45-2(clock run-in)-1(framing code)=42 bytes
1442 for(i=1; i<=(42<<3); i++){
1443 r>>=1;
1444 x=FIXP2INT(xFixp);
1445 if(buf[x]> avg)
1446 r|=0x80;
1448 if(!(i & 0x07)){
1449 data[(i>>3)-1]=r;
1450 r=0;
1452 xFixp+=priv->bpb;
1455 return 1;
1457 #endif
1460 * \brief decodes all vbi lines from one video frame
1461 * \param priv private data structure
1462 * \param buf buffer with raw vbi data in it
1464 * \note buffer size have to be at least priv->ptsp->bufsize bytes
1466 static void vbi_decode(priv_vbi_t* priv,unsigned char*buf){
1467 int magAddr;
1468 int pkt;
1469 unsigned char data[64];
1470 unsigned char* linep;
1471 int d0,d1;
1472 int i=0;
1473 mp_msg(MSGT_TV,MSGL_DBG3,"vbi: vbi_decode\n");
1474 for(linep=buf; !priv->cache_reset && linep<buf+priv->ptsp->bufsize; linep+=priv->ptsp->samples_per_line,i++){
1475 #if 0
1477 This routine is alternative implementation of raw VBI data decoding.
1478 Unfortunately, it detects only about 20% of incoming data,
1479 but Michael says that this algorithm is better, and he wants to fix it.
1481 if(decode_raw_line_sine(priv,linep,data)<=0){
1482 #endif
1483 if(decode_raw_line_runin(priv,linep,data)<=0){
1484 continue; //this is not valid teletext line
1486 d0= corrHamm48[ data[0] ];
1487 d1= corrHamm48[ data[1] ];
1489 if(d0&0x80 || d1&0x80){
1490 pll_add(priv,2,4);
1491 mp_msg(MSGT_TV,MSGL_V,"vbi_decode(%d):HammErr after decode_raw_line\n",i);
1493 continue; //hamError
1495 magAddr=d0 & 0x7;
1496 pkt=(d0>>3)|(d1<<1);
1497 mp_msg(MSGT_TV,MSGL_DBG3,"vbi_decode(%d):%x %x (mag:%x, pkt:%d)\n",
1498 i,d0,d1,magAddr,pkt);
1499 if(!pkt){
1500 decode_pkt0(priv,data+2,magAddr); //skip MRGA
1501 }else if(pkt>0 && pkt<VBI_ROWS){
1502 if(!priv->mag[magAddr].pt) continue;
1503 decode_pkt_page(priv,data+2,magAddr,pkt);//skip MRGA
1504 }else if(pkt==27) {
1505 decode_pkt27(priv,data+2,magAddr);
1506 }else if(pkt==28){
1507 decode_pkt28(priv,data+2);
1508 }else if(pkt==30){
1509 decode_pkt30(priv,data+2,magAddr);
1510 } else {
1511 mp_msg(MSGT_TV,MSGL_DBG3,"unsupported packet:%d\n",pkt);
1514 if (priv->cache_reset){
1515 pthread_mutex_lock(&(priv->buffer_mutex));
1516 priv->cache_reset--;
1517 pthread_mutex_unlock(&(priv->buffer_mutex));
1523 ---------------------------------------------------------------------------------
1524 Public routines
1525 ---------------------------------------------------------------------------------
1529 * \brief toggles teletext page displaying format
1530 * \param priv_vbi private data structure
1531 * \param flag new format
1532 * \return
1533 * TVI_CONTROL_TRUE is success,
1534 * TVI_CONTROL_FALSE otherwise
1536 * flag:
1537 * 0 - opaque
1538 * 1 - transparent
1539 * 2 - opaque with black foreground color (only in bw mode)
1540 * 3 - transparent with black foreground color (only in bw mode)
1542 static int teletext_set_format(priv_vbi_t * priv, teletext_format flag)
1544 flag&=3;
1546 mp_msg(MSGT_TV,MSGL_DBG3,"teletext_set_format_is called. mode:%d\n",flag);
1547 pthread_mutex_lock(&(priv->buffer_mutex));
1549 priv->tformat=flag;
1551 priv->pagenumdec=0;
1553 pthread_mutex_unlock(&(priv->buffer_mutex));
1554 return TVI_CONTROL_TRUE;
1558 * \brief append just entered digit to editing page number
1559 * \param priv_vbi private data structure
1560 * \param dec decimal digit to append
1562 * dec:
1563 * '0'..'9' append digit
1564 * '-' remove last digit (backspace emulation)
1566 * This routine allows user to jump to arbitrary page.
1567 * It implements simple page number editing algorithm.
1569 * Subsystem can be on one of two modes: normal and page number edit mode.
1570 * Zero value of priv->pagenumdec means normal mode
1571 * Non-zero value means page number edit mode and equals to packed
1572 * decimal number of already entered part of page number.
1574 * How this works.
1575 * Let's assume that current mode is normal (pagenumdec is zero), teletext page
1576 * 100 are displayed as usual. topmost left corner of page contains page number.
1577 * Then vbi_add_dec is sequentially called (through slave
1578 * command of course) with 1,4,-,2,3 * values of dec parameter.
1580 * +-----+------------+------------------+
1581 * | dec | pagenumdec | displayed number |
1582 * +-----+------------+------------------+
1583 * | | 0x000 | 100 |
1584 * +-----+------------+------------------+
1585 * | 1 | 0x001 | __1 |
1586 * +-----+------------+------------------+
1587 * | 4 | 0x014 | _14 |
1588 * +-----+------------+------------------+
1589 * | - | 0x001 | __1 |
1590 * +-----+------------+------------------+
1591 * | 2 | 0x012 | _12 |
1592 * +-----+------------+------------------+
1593 * | 3 | 0x123 | 123 |
1594 * +-----+------------+------------------+
1595 * | | 0x000 | 123 |
1596 * +-----+------------+------------------+
1598 * pagenumdec will automatically receive zero value after third digit of page
1599 * number is entered and current page will be switched to another one with
1600 * entered page number.
1602 static void vbi_add_dec(priv_vbi_t * priv, char *dec)
1604 int count, shift;
1605 if (!dec)
1606 return;
1607 if (!priv->on)
1608 return;
1609 if ((*dec<'0' || *dec>'9') && *dec!='-')
1610 return;
1611 if (!priv->pagenumdec) //first digit cannot be '0','9' or '-'
1612 if(*dec=='-' || *dec=='0' || *dec=='9')
1613 return;
1614 pthread_mutex_lock(&(priv->buffer_mutex));
1615 count=(priv->pagenumdec>>12)&0xf;
1616 if (*dec=='-') {
1617 count--;
1618 if (count)
1619 priv->pagenumdec=((priv->pagenumdec>>4)&0xfff)|(count<<12);
1620 else
1621 priv->pagenumdec=0;
1622 } else {
1623 shift = count * 4;
1624 count++;
1625 priv->pagenumdec=
1626 (((priv->pagenumdec)<<4|(*dec-'0'))&0xfff)|(count<<12);
1627 if (count==3) {
1628 priv->pagenum=priv->pagenumdec&0x7ff;
1629 priv->subpagenum=get_subpagenum_from_cache(priv,priv->pagenum);
1630 priv->pagenumdec=0;
1633 pthread_mutex_unlock(&(priv->buffer_mutex));
1638 * \brief Teletext control routine
1639 * \param priv_vbi private data structure
1640 * \param cmd command
1641 * \param arg command parameter (has to be not null)
1643 int teletext_control(void* p, int cmd, void *arg)
1645 int fine_tune=99;
1646 priv_vbi_t* priv=(priv_vbi_t*)p;
1647 tt_page* pgc;
1649 if (!priv && cmd!=TV_VBI_CONTROL_START)
1650 return TVI_CONTROL_FALSE;
1651 if (!arg && cmd!=TV_VBI_CONTROL_STOP && cmd!=TV_VBI_CONTROL_MARK_UNCHANGED)
1652 return TVI_CONTROL_FALSE;
1654 switch (cmd) {
1655 case TV_VBI_CONTROL_RESET:
1657 int i;
1658 tv_param_t* tv_param=arg;
1659 pthread_mutex_lock(&(priv->buffer_mutex));
1660 priv->pagenumdec=0;
1661 clear_cache(priv);
1662 priv->pagenum=steppage(0,tv_param->tpage&0x7ff,1);
1663 priv->tformat=tv_param->tformat;
1664 priv->subpagenum=0x3f7f;
1665 pll_reset(priv,fine_tune);
1666 if(tv_param->tlang==-1){
1667 mp_msg(MSGT_TV,MSGL_INFO,MSGTR_TV_TTSupportedLanguages);
1668 for(i=0; tt_languages[i].lang_code; i++){
1669 mp_msg(MSGT_TV,MSGL_INFO," %3d %s\n",
1670 tt_languages[i].lang_code, tt_languages[i].lang_name);
1672 mp_msg(MSGT_TV,MSGL_INFO," %3d %s\n",
1673 tt_languages[i].lang_code, tt_languages[i].lang_name);
1674 }else{
1675 for(i=0; tt_languages[i].lang_code; i++){
1676 if(tt_languages[i].lang_code==tv_param->tlang)
1677 break;
1679 if (priv->primary_language!=tt_languages[i].lang_code){
1680 mp_msg(MSGT_TV,MSGL_INFO,MSGTR_TV_TTSelectedLanguage,
1681 tt_languages[i].lang_name);
1682 priv->primary_language=tt_languages[i].lang_code;
1685 priv->page_changed=1;
1686 pthread_mutex_unlock(&(priv->buffer_mutex));
1687 return TVI_CONTROL_TRUE;
1689 case TV_VBI_CONTROL_START:
1691 int i;
1692 tt_stream_props* ptsp=*(tt_stream_props**)arg;
1694 if(!ptsp)
1695 return TVI_CONTROL_FALSE;
1697 priv=calloc(1,sizeof(priv_vbi_t));
1699 priv->ptsp=malloc(sizeof(tt_stream_props));
1700 memcpy(priv->ptsp,ptsp,sizeof(tt_stream_props));
1701 *(priv_vbi_t**)arg=priv;
1703 priv->subpagenum=0x3f7f;
1704 pthread_mutex_init(&priv->buffer_mutex, NULL);
1705 priv->pagenumdec=0;
1706 for(i=0;i<VBI_ROWS*VBI_COLUMNS;i++)
1707 priv->display_page[i]=tt_space;
1709 priv->mag=calloc(8,sizeof(mag_t));
1710 init_cache(priv);
1711 init_vbi_consts(priv);
1712 pll_reset(priv,fine_tune);
1713 priv->page_changed=1;
1714 return TVI_CONTROL_TRUE;
1716 case TV_VBI_CONTROL_STOP:
1718 if(priv->mag)
1719 free(priv->mag);
1720 if(priv->ptsp)
1721 free(priv->ptsp);
1722 destroy_cache(priv);
1723 priv->page_changed=1;
1724 free(priv);
1725 return TVI_CONTROL_TRUE;
1727 case TV_VBI_CONTROL_SET_MODE:
1728 priv->on=(*(int*)arg%2);
1729 priv->page_changed=1;
1730 return TVI_CONTROL_TRUE;
1731 case TV_VBI_CONTROL_GET_MODE:
1732 *(int*)arg=priv->on;
1733 return TVI_CONTROL_TRUE;
1734 case TV_VBI_CONTROL_SET_FORMAT:
1735 priv->page_changed=1;
1736 return teletext_set_format(priv, *(int *) arg);
1737 case TV_VBI_CONTROL_GET_FORMAT:
1738 pthread_mutex_lock(&(priv->buffer_mutex));
1739 *(int*)arg=priv->tformat;
1740 pthread_mutex_unlock(&(priv->buffer_mutex));
1741 return TVI_CONTROL_TRUE;
1742 case TV_VBI_CONTROL_GET_HALF_PAGE:
1743 if(!priv->on)
1744 return TVI_CONTROL_FALSE;
1745 *(int *)arg=priv->zoom;
1746 return TVI_CONTROL_TRUE;
1747 case TV_VBI_CONTROL_SET_HALF_PAGE:
1749 int val=*(int*)arg;
1750 val%=3;
1751 if(val<0)
1752 val+=3;
1753 pthread_mutex_lock(&(priv->buffer_mutex));
1754 priv->zoom=val;
1755 priv->page_changed=1;
1756 pthread_mutex_unlock(&(priv->buffer_mutex));
1757 return TVI_CONTROL_TRUE;
1759 case TV_VBI_CONTROL_GO_LINK:
1761 int val=*(int *) arg;
1762 if(val<1 || val>6)
1763 return TVI_CONTROL_FALSE;
1764 pthread_mutex_lock(&(priv->buffer_mutex));
1765 if (!(pgc = priv->ptt_cache[priv->pagenum])) {
1766 pthread_mutex_unlock(&(priv->buffer_mutex));
1767 return TVI_CONTROL_FALSE;
1769 if (!pgc->links[val-1].pagenum || pgc->links[val-1].pagenum>0x7ff) {
1770 pthread_mutex_unlock(&(priv->buffer_mutex));
1771 return TVI_CONTROL_FALSE;
1773 priv->pagenum=pgc->links[val-1].pagenum;
1774 if(pgc->links[val-1].subpagenum!=0x3f7f)
1775 priv->subpagenum=pgc->links[val-1].subpagenum;
1776 else
1777 priv->subpagenum=get_subpagenum_from_cache(priv,priv->pagenum);
1778 priv->page_changed=1;
1779 pthread_mutex_unlock(&(priv->buffer_mutex));
1780 return TVI_CONTROL_TRUE;
1782 case TV_VBI_CONTROL_SET_PAGE:
1784 int val=*(int *) arg;
1785 if(val<100 || val>0x899)
1786 return TVI_CONTROL_FALSE;
1787 pthread_mutex_lock(&(priv->buffer_mutex));
1788 priv->pagenum=val&0x7ff;
1789 priv->subpagenum=get_subpagenum_from_cache(priv,priv->pagenum);
1790 priv->pagenumdec=0;
1791 priv->page_changed=1;
1792 pthread_mutex_unlock(&(priv->buffer_mutex));
1793 return TVI_CONTROL_TRUE;
1795 case TV_VBI_CONTROL_STEP_PAGE:
1797 int direction=*(int *) arg;
1798 pthread_mutex_lock(&(priv->buffer_mutex));
1799 priv->pagenum=steppage(priv->pagenum, direction,1);
1800 priv->subpagenum=get_subpagenum_from_cache(priv,priv->pagenum);
1801 priv->pagenumdec=0;
1802 priv->page_changed=1;
1803 pthread_mutex_unlock(&(priv->buffer_mutex));
1804 return TVI_CONTROL_TRUE;
1806 case TV_VBI_CONTROL_GET_PAGE:
1807 *(int*)arg=((priv->pagenum+0x700)&0x7ff)+0x100;
1808 return TVI_CONTROL_TRUE;
1809 case TV_VBI_CONTROL_SET_SUBPAGE:
1810 pthread_mutex_lock(&(priv->buffer_mutex));
1811 priv->pagenumdec=0;
1812 priv->subpagenum=*(int*)arg;
1813 if(priv->subpagenum<0)
1814 priv->subpagenum=0x3f7f;
1815 if(priv->subpagenum>=VBI_MAX_SUBPAGES)
1816 priv->subpagenum=VBI_MAX_SUBPAGES-1;
1817 priv->page_changed=1;
1818 pthread_mutex_unlock(&(priv->buffer_mutex));
1819 return TVI_CONTROL_TRUE;
1820 case TV_VBI_CONTROL_GET_SUBPAGE:
1821 *(int*)arg=priv->subpagenum;
1822 return TVI_CONTROL_TRUE;
1823 case TV_VBI_CONTROL_ADD_DEC:
1824 vbi_add_dec(priv, *(char **) arg);
1825 priv->page_changed=1;
1826 return TVI_CONTROL_TRUE;
1827 case TV_VBI_CONTROL_DECODE_PAGE:
1828 vbi_decode(priv,*(unsigned char**)arg);
1829 return TVI_CONTROL_TRUE;
1830 case TV_VBI_CONTROL_GET_VBIPAGE:
1831 if(!priv->on)
1832 return TVI_CONTROL_FALSE;
1833 prepare_visible_page(priv);
1834 *(void **)arg=priv->display_page;
1835 return TVI_CONTROL_TRUE;
1836 case TV_VBI_CONTROL_GET_NETWORKNAME:
1837 *(void **)arg=priv->networkname;
1838 return TVI_CONTROL_TRUE;
1839 case TV_VBI_CONTROL_MARK_UNCHANGED:
1840 priv->page_changed=0;
1841 priv->last_rendered=GetTimerMS();
1842 return TVI_CONTROL_TRUE;
1843 case TV_VBI_CONTROL_IS_CHANGED:
1844 if(GetTimerMS()-priv->last_rendered> 250) //forcing page update every 1/4 sec
1845 priv->page_changed=3; //mark that header update is enough
1846 *(int*)arg=priv->page_changed;
1847 return TVI_CONTROL_TRUE;
1849 return TVI_CONTROL_UNKNOWN;