ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Video-Capture-V4l/VBI/VBI.xs
Revision: 1.2
Committed: Thu May 11 01:22:57 2000 UTC (24 years, 2 months ago) by pcg
Branch: MAIN
Changes since 1.1: +1 -3 lines
Log Message:
*** empty log message ***

File Contents

# Content
1 #include "EXTERN.h"
2 #include "perl.h"
3 #include "XSUB.h"
4
5 #include <sys/types.h>
6 #include <unistd.h>
7 #include <sys/mman.h>
8
9 #include "../gppport.h"
10
11 /* loosely based on the program vbidecode.cc by Ralph Metzler */
12
13 typedef unsigned int UI;
14 typedef unsigned char u8;
15 typedef U16 u16;
16
17 /* calculates odd parity, medium-efficient */
18 static int
19 parodd(U32 data)
20 {
21 u8 p4[16] = { 0,1,1,0, 1,0,0,1, 1,0,0,1, 0,1,1,0 };
22 int parity = 1;
23
24 do {
25 parity ^= p4[data & 15];
26 data >>= 4;
27 } while (data);
28
29 return parity;
30 }
31
32 static u8 invtab[256] = {
33 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
34 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
35 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
36 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
37 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
38 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
39 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
40 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
41 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
42 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
43 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
44 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
45 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
46 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
47 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
48 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
49 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
50 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
51 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
52 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
53 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
54 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
55 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
56 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
57 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
58 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
59 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
60 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
61 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
62 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
63 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
64 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff,
65 };
66
67 static u8 unhamtab[256] = {
68 0x01, 0xff, 0x81, 0x01, 0xff, 0x00, 0x01, 0xff,
69 0xff, 0x02, 0x01, 0xff, 0x0a, 0xff, 0xff, 0x07,
70 0xff, 0x00, 0x01, 0xff, 0x00, 0x80, 0xff, 0x00,
71 0x06, 0xff, 0xff, 0x0b, 0xff, 0x00, 0x03, 0xff,
72 0xff, 0x0c, 0x01, 0xff, 0x04, 0xff, 0xff, 0x07,
73 0x06, 0xff, 0xff, 0x07, 0xff, 0x07, 0x07, 0x87,
74 0x06, 0xff, 0xff, 0x05, 0xff, 0x00, 0x0d, 0xff,
75 0x86, 0x06, 0x06, 0xff, 0x06, 0xff, 0xff, 0x07,
76 0xff, 0x02, 0x01, 0xff, 0x04, 0xff, 0xff, 0x09,
77 0x02, 0x82, 0xff, 0x02, 0xff, 0x02, 0x03, 0xff,
78 0x08, 0xff, 0xff, 0x05, 0xff, 0x00, 0x03, 0xff,
79 0xff, 0x02, 0x03, 0xff, 0x03, 0xff, 0x83, 0x03,
80 0x04, 0xff, 0xff, 0x05, 0x84, 0x04, 0x04, 0xff,
81 0xff, 0x02, 0x0f, 0xff, 0x04, 0xff, 0xff, 0x07,
82 0xff, 0x05, 0x05, 0x85, 0x04, 0xff, 0xff, 0x05,
83 0x06, 0xff, 0xff, 0x05, 0xff, 0x0e, 0x03, 0xff,
84 0xff, 0x0c, 0x01, 0xff, 0x0a, 0xff, 0xff, 0x09,
85 0x0a, 0xff, 0xff, 0x0b, 0x8a, 0x0a, 0x0a, 0xff,
86 0x08, 0xff, 0xff, 0x0b, 0xff, 0x00, 0x0d, 0xff,
87 0xff, 0x0b, 0x0b, 0x8b, 0x0a, 0xff, 0xff, 0x0b,
88 0x0c, 0x8c, 0xff, 0x0c, 0xff, 0x0c, 0x0d, 0xff,
89 0xff, 0x0c, 0x0f, 0xff, 0x0a, 0xff, 0xff, 0x07,
90 0xff, 0x0c, 0x0d, 0xff, 0x0d, 0xff, 0x8d, 0x0d,
91 0x06, 0xff, 0xff, 0x0b, 0xff, 0x0e, 0x0d, 0xff,
92 0x08, 0xff, 0xff, 0x09, 0xff, 0x09, 0x09, 0x89,
93 0xff, 0x02, 0x0f, 0xff, 0x0a, 0xff, 0xff, 0x09,
94 0x88, 0x08, 0x08, 0xff, 0x08, 0xff, 0xff, 0x09,
95 0x08, 0xff, 0xff, 0x0b, 0xff, 0x0e, 0x03, 0xff,
96 0xff, 0x0c, 0x0f, 0xff, 0x04, 0xff, 0xff, 0x09,
97 0x0f, 0xff, 0x8f, 0x0f, 0xff, 0x0e, 0x0f, 0xff,
98 0x08, 0xff, 0xff, 0x05, 0xff, 0x0e, 0x0d, 0xff,
99 0xff, 0x0e, 0x0f, 0xff, 0x0e, 0x8e, 0xff, 0x0e,
100 };
101
102 #define VBI_BPL 2048
103
104 #define FREQ_PAL 35.468950
105 #define FREQ_NTSC 28.636363
106 #define FREQ FREQ_PAL
107
108 #define FREQ_VT_PAL 6.9375
109 #define FREQ_VT_NTSC 5.72725
110 #define FREQ_VT FREQ_VT_PAL /* Replace by FREQ_VT_NTSC and pray that it works */
111 /*#define FREQ_VT 6.165*/ /* teletext-like signal on france, 0xe7 instead of 0x27 */
112 #define FREQ_CRYPT 4.5 /* found on premiere */
113 #define FREQ_VPS 2.5 /* probably only pal */
114 #define FREQ_VDAT 2.0 /* videodat */
115 #define FREQ_VC 0.77 /* videocrypt, just ignore */
116
117 #define VBI_VT 0x0001
118 #define VBI_VPS 0x0002
119 #define VBI_VDAT 0x0004
120 #define VBI_VC 0x0008
121 #define VBI_OTHER 0x0010
122 #define VBI_EMPTY 0x8000
123
124 typedef long FP;
125
126 #define FP_BITS 16
127 #define FP_0_5 D2FP(0.5)
128
129 #define D2FP(d) ((FP)((d) * (1<<FP_BITS) + 0.5))
130 #define I2FP(i) ((FP)(i) << FP_BITS)
131 #define FP2I(fp) (((fp) + FP_0_5) >> FP_BITS)
132
133 #define VT_COLS 40
134 #define VT_LINES 36
135
136 typedef struct {
137 UI types; /* required types */
138
139 int offset; /* signal offset */
140 int did_agc : 1; /* did we already do agc this frame? */
141
142 int y; /* the line number */
143 u8 *line; /* the current line */
144 FP step; /* the bit step */
145 FP pos; /* position */
146 } decoder;
147
148 static void
149 decoder_init (decoder *dec, UI types)
150 {
151 dec->types = types;
152 dec->did_agc = 0;
153 }
154
155 static void
156 decoder_scan_start (decoder *dec, UI a, UI b)
157 {
158 u8 *p = dec->line + a;
159 UI med = 128 - dec->offset;
160 do
161 {
162 if (*p >= med)
163 break;
164 }
165 while (++p < dec->line + b);
166
167 /* find maximum */
168 while (p[1] > p[0])
169 p++;
170
171 dec->pos = I2FP (p - dec->line);
172 }
173
174
175 static u8
176 get_byte (decoder *dec)
177 {
178 u8 byte;
179 int bit = 8;
180
181 /* if the next bit is a one bit, try to re-center the decoder on it */
182 if ((dec->offset + dec->line[FP2I(dec->pos)]) & 0x80)
183 {
184 /*if (dec->line[FP2I(dec->pos)] < dec->line[FP2I(dec->pos)+1])
185 dec->pos += I2FP(1);*/ /* why is this casuing checksum errors? */
186 /*if (dec->line[FP2I(dec->pos)] < dec->line[FP2I(dec->pos)-1])
187 dec->pos -= I2FP(1);*/
188 }
189
190 byte=0;
191 do
192 {
193 byte >>= 1;
194 byte |= ((dec->offset + dec->line[FP2I(dec->pos)]) & 0x80);
195 dec->pos += dec->step;
196 }
197 while (--bit);
198
199 return byte;
200 }
201
202 /* get shift-encoded byte */
203 static u8
204 get_byte_SE (decoder *dec)
205 {
206 u8 byte;
207 int bit = 8;
208
209 do
210 {
211 byte >>= 1;
212 byte |= (dec->line[FP2I(dec->pos)]
213 > dec->line[FP2I(dec->pos + dec->step/2)]) << 7;
214 dec->pos += dec->step;
215 }
216 while (--bit);
217
218 /* if the next bit is a one bit, try to re-center the decoder on it */
219 if (dec->line[FP2I(dec->pos)] > 128-dec->offset)
220 {
221 if (dec->line[FP2I(dec->pos)] > dec->line[FP2I(dec->pos)+1])
222 dec->pos += I2FP(1);
223 if (dec->line[FP2I(dec->pos)] < dec->line[FP2I(dec->pos)-1])
224 dec->pos -= I2FP(1);
225 }
226
227 return byte;
228 }
229
230 static u8
231 unham4(u8 a)
232 {
233 return unhamtab[a] & 15;
234 }
235
236 static u8
237 unham8(u8 a, u8 b)
238 {
239 u8 c1 = unhamtab[a];
240 u8 c2 = unhamtab[b];
241
242 if ( (c1|c2) & 0x40)
243 /* 2 bit error */;
244
245 return (c1 & 15)
246 | (c2 << 4);
247 }
248
249 static u16 unham16(u8 a, u8 b, u8 c)
250 {
251 U32 d = (((c << 8) | b) << 8) | c;
252 int A = parodd (d & 0x555555);
253 int B = parodd (d & 0x666666);
254 int C = parodd (d & 0x787878);
255 int D = parodd (d & 0x007f80);
256 int E = parodd (d & 0x7f8000);
257 int F = parodd (d & 0xffffff);
258 int bit;
259
260 d = (((d >> 16) & 0x7f) << 11)
261 | (((d >> 8) & 0x7f) << 4)
262 | (((d >> 4) & 0x07) << 1)
263 | (((d >> 2) & 0x01) );
264
265 if (A&B&C&D&E)
266 return d;
267 if (F)
268 return -1; /* double bit error */
269
270 /* correct the single bit error */
271 return d ^ (1 << (31 - 16*E + 8*D + 4*C + 2*B + A));
272 }
273
274 static unsigned char
275 rev (unsigned char x)
276 {
277 return ((x>>7)&1)<<0 |
278 ((x>>6)&1)<<1 |
279 ((x>>5)&1)<<2 |
280 ((x>>4)&1)<<3 |
281 ((x>>3)&1)<<4 |
282 ((x>>2)&1)<<5 |
283 ((x>>1)&1)<<6 |
284 ((x>>0)&1)<<7;
285 }
286
287 static SV *
288 decoder_decode_vps (decoder *dec, u8 *data)
289 {
290 AV *av = newAV ();
291
292 av_push (av, newSViv (VBI_VPS));
293 av_push (av, newSVpvn (data+3, 1));
294 av_push (av, newSViv (data[4] & 3)); /* "unknown", "stereo ", "mono ", "dual " */
295 /* ch, day, mon, hour, min */
296 av_push (av, newSViv (rev (data[ 4]) <<12 & 0xf000
297 | rev (data[10]) & 0x00c0
298 | rev (data[12]) <<10 & 0x0c00
299 | rev (data[13]) << 2 & 0x0300
300 | rev (data[13]) & 0x003f));
301 av_push (av, newSViv (rev (data[10]) >> 1 & 31));
302 av_push (av, newSViv (rev (data[10]) << 3 & 8 | rev (data[11]) >> 5));
303 av_push (av, newSViv (rev (data[11]) & 31));
304 av_push (av, newSViv (rev (data[12]) >> 2));
305 av_push (av, newSViv (rev (data[14])));
306
307 return newRV_noinc ((SV*)av);
308 }
309
310 static SV *
311 decoder_decode_vt (decoder *dec, u8 *data)
312 {
313 AV *av = newAV ();
314 u8 mpag = unham8 (data[3], data[4]);
315 u8 mag = mpag & 7;
316 u8 pack = (mpag & 0xf8) >> 3;
317
318 av_push (av, newSViv (VBI_VT));
319 av_push (av, newSViv (mag));
320 av_push (av, newSViv (pack));
321
322 switch (pack)
323 {
324 /* ets300-706 */
325 case 0:
326 av_push (av, newSVpvn (data+5, 40));
327 av_push (av, newSViv (unham8 (data[5], data[6]) | (mag << 8)));
328 av_push (av, newSViv (unham8 (data[7], data[8])
329 | (unham8 (data[9], data[10]) << 8)
330 | (unham8 (data[11], data[12]) << 16)));
331 break;
332 case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8: case 9: case 10:
333 case 11: case 12: case 13: case 14: case 15: case 16: case 17: case 18: case 19: case 20:
334 case 21: case 22: case 23: case 24: case 25:
335 av_push (av, newSVpvn (data+5, 40));
336 break;
337 case 26: case 27: case 28: case 29: /* enhancement packets */
338 av_push (av, newSViv (unham4 (data[5])));
339 av_push (av, newSViv (unham16 (data[ 6], data[ 7], data[ 8])));
340 av_push (av, newSViv (unham16 (data[ 9], data[10], data[11])));
341 av_push (av, newSViv (unham16 (data[12], data[13], data[14])));
342 av_push (av, newSViv (unham16 (data[15], data[16], data[17])));
343 av_push (av, newSViv (unham16 (data[18], data[19], data[20])));
344 av_push (av, newSViv (unham16 (data[21], data[22], data[23])));
345 av_push (av, newSViv (unham16 (data[24], data[25], data[26])));
346 av_push (av, newSViv (unham16 (data[27], data[28], data[29])));
347 av_push (av, newSViv (unham16 (data[30], data[31], data[32])));
348 av_push (av, newSViv (unham16 (data[33], data[34], data[35])));
349 av_push (av, newSViv (unham16 (data[36], data[37], data[38])));
350 av_push (av, newSViv (unham16 (data[39], data[40], data[41])));
351 av_push (av, newSViv (unham16 (data[42], data[43], data[44])));
352 break;
353 /* ets300-706 & ets300-231 */
354 case 30:
355 {
356 UI dc = unham4 (data[5]);
357 av_push (av, newSViv (dc));
358 if ((dc >> 1) == 0)
359 {
360 av_push (av, newSViv (unham8 (data[6], data[7]) | (mag << 8)));
361 av_push (av, newSViv (unham8 (data[8], data[9])
362 | unham8 (data[10], data[11]) << 8));
363 av_push (av, newSViv (rev (data[12]) << 8 | rev (data[13])));
364 }
365 else if ((dc >> 1) == 8)
366 {
367 /* pdc */
368 }
369 }
370 break;
371 case 31:
372 {
373 UI ft = unham4 (data[5]);
374 UI al = unham4 (data[6]);
375 UI i, addr = 0;
376
377 /* ets300-708, IDL */
378 /* http://sunsite.cnlab-switch.ch/ftp/mirror/internet-drafts/draft-ietf-ipvbi-tv-signal-00.txt */
379
380 for(i=0; i<al&7; i++)
381 addr = addr << 4 | unham4 (data[i+7]);
382
383 av_push (av, newSViv (ft));
384 av_push (av, newSViv (addr));
385 break;
386 }
387 default:
388 av_push (av, newSVpvn (data+5, 40));
389 }
390 return newRV_noinc ((SV*)av);
391 }
392
393 static SV *
394 decoder_decode_other (decoder *dec)
395 {
396 AV *av = newAV ();
397 FP pos = dec->pos;
398 u8 data[6];
399
400 av_push (av, newSViv (VBI_OTHER));
401
402 dec->step = D2FP (FREQ / FREQ_CRYPT); /* found on premiere */
403 data [0] = get_byte (dec);
404 data [1] = get_byte (dec);
405 data [2] = get_byte (dec);
406 if (data[0] == 0x55 && data[1] == 0xd0 && data[2] == 0x18)
407 {
408 /* premiere */
409 av_push (av, newSViv (1));
410 }
411 else
412 av_push (av, newSViv (0));
413
414 return newRV_noinc ((SV*)av);
415 }
416
417 static SV *
418 decoder_decode_empty (decoder *dec)
419 {
420 AV *av = newAV ();
421 av_push (av, newSViv (VBI_EMPTY));
422 return newRV_noinc ((SV*)av);
423 }
424
425 static SV *
426 decoder_decode (decoder *dec, UI y, u8 *line)
427 {
428 UI type;
429 u8 data[45]; /* 45 bytes per line are max. */
430 UI i;
431 int did_agc;
432
433 type = VBI_VT | VBI_EMPTY | VBI_OTHER | VBI_VC; /* can be everywhere */
434 if (y == 16 - 7) type |= VBI_VPS;
435 if (y > 17 - 7) type |= VBI_VDAT;
436
437 type &= dec->types;
438
439 /* don't do anything unless we need to */
440 if (type)
441 {
442 dec->line = line;
443 dec->y = y;
444 dec->pos = 0;
445
446 did_agc = dec->did_agc;
447
448 /* maybe do agc? */
449 if (!dec->did_agc || y == 20-7)
450 {
451 u8 *n = line + 120;
452 u8 max = 0, min = 255;
453 do
454 {
455 if (*n > max) max = *n;
456 if (*n < min) min = *n;
457 }
458 while (++n < line + 300);
459
460 if (max > min + 30)
461 {
462 dec->offset = 128 - (((int)max + (int)min) >> 1);
463 dec->did_agc = 1;
464 }
465 }
466
467 if (dec->did_agc)
468 {
469 if (type & VBI_VT)
470 {
471 dec->step = D2FP (FREQ / FREQ_VT);
472 decoder_scan_start (dec, 50, 350);
473
474 data[0] = get_byte (dec);
475 if ((data[0] & 0xfe) == 0x54)
476 {
477 data[1] = get_byte (dec);
478 switch (data[1])
479 {
480 case 0x27:
481 dec->pos -= dec->step * 2;
482 case 0x4e:
483 dec->pos -= dec->step * 2;
484 case 0x9d:
485 dec->pos -= dec->step * 2;
486 case 0x75:
487 dec->pos -= dec->step * 2;
488 case 0xd5:
489 dec->pos -= dec->step * 2;
490 data[1] = 0x55;
491 case 0x55:
492 break;
493 default:
494 /* no teletext page */
495 }
496
497 if (data[1] == 0x55)
498 {
499 data[2] = get_byte (dec);
500 if (data[2] == 0x27 || data[2] == 0xe7)
501 {
502 for (i = 3; i < 45; i++)
503 data[i] = get_byte (dec);
504 return decoder_decode_vt (dec, data);
505 }
506 }
507 }
508 }
509
510 if (type & VBI_VPS)
511 {
512 decoder_scan_start (dec, 150, 260);
513 dec->step = D2FP (FREQ / FREQ_VPS); /* shift encoding, two "pixels"/bit (skip the 2nd) */
514 data[0] = get_byte_SE (dec);
515 if (data[0] == 0xff)
516 {
517 data[1] = get_byte_SE (dec);
518 if (data[1] == 0x5d || data[1] == 0x5f)
519 {
520 for (i = 2; i < 16; i++)
521 data[i] = get_byte_SE (dec);
522 return decoder_decode_vps (dec, data);
523 }
524 }
525 }
526
527 #if 0
528 if (type & VBI_VDAT)
529 {
530 dec->step = D2FP (FREQ / FREQ_VDAT);
531 decoder_scan_start (dec, 150, 200);
532 }
533
534 if (type & VBI_VC)
535 {
536 dec->step = D2FP (FREQ / FREQ_VC);
537 decoder_scan_start (dec, 150, 200);
538 }
539 #endif
540
541 /* watch out for empty lines, tets signals etc.. */
542 if (type & VBI_OTHER)
543 {
544 dec->did_agc = did_agc; /* other signals do not affect agc, yet */
545
546 dec->step = D2FP (FREQ);
547 decoder_scan_start (dec, 100, 500);
548 if (dec->pos < I2FP (450))
549 return decoder_decode_other (dec);
550 }
551 }
552
553 dec->did_agc = did_agc;
554
555 if (type & VBI_EMPTY)
556 return decoder_decode_empty (dec);
557 }
558
559 return 0;
560 }
561
562 /* vtx decoding routines taken from videotext-0.6.971023,
563 * Copyright (c) 1994-96 Martin Buck */
564
565 #define VTX_COLMASK 0x07
566 #define VTX_BGMASK (0x07 << 3)
567 #define VTX_G1 (1 << 6)
568 #define VTX_GRSEP (1 << 8)
569 #define VTX_HIDDEN (1 << 9)
570 #define VTX_BOX (1 << 10)
571 #define VTX_FLASH (1 << 11)
572 #define VTX_DOUBLE1 (1 << 12)
573 #define VTX_DOUBLE2 (1 << 13)
574 #define VTX_INVERT (1 << 14)
575 #define VTX_DOUBLE (VTX_DOUBLE1 | VTX_DOUBLE2)
576
577 static const u8 g0_to_iso_table[256] =
578 " "
579 " !\"£$%&'()*+,-./0123456789:;<=>?"
580 "@ABCDEFGHIJKLMNOPQRSTUVWXYZÄÖÜ^#"
581 "-abcdefghijklmnopqrstuvwxyzäöüß#"
582 " "
583 " "
584 " "
585 " ";
586
587 /* one-to-one copy */
588 static int
589 decode_vtpage (u8 *src, UI lines, u8 *chr, u16 *attr)
590 {
591 UI line, col, pos, graphics, grhold, doubleht, nextattr = 0;
592 u16 *lastattr, default_attrib = 7, next_attrib;
593 u8 c, *lastchr, default_chr = ' ';
594 UI lang;
595
596 lang = 4;
597 pos = 0;
598 doubleht = 0;
599
600 for (line = 0; line < lines; line++) {
601 lastchr = &default_chr;
602 lastattr = &default_attrib;
603 graphics = grhold = 0;
604 if (doubleht && pos > 40) {
605 for (col = 0; col <= 39; col++) {
606 if (attr[pos - 40] & VTX_DOUBLE1) {
607 chr[pos] = chr[pos - 40];
608 chr[pos - 40] = ' ';
609 attr[pos] = (attr[pos - 40] & ~VTX_DOUBLE1) | VTX_DOUBLE2;
610 } else {
611 chr[pos] = ' ';
612 attr[pos] = attr[pos - 40];
613 }
614 pos++;
615 }
616 doubleht = 0;
617 } else {
618 for (col = 0; col <= 39; col++) {
619 c = src[pos];
620 if (parodd(c)) {
621 chr[pos] = 254; /* Parity error */
622 attr[pos] = 7;
623 /* return 0 */ /*?*/
624 } else if ((c & 0x7f) >= 32) { /* Normal character */
625 c &= 0x7f;
626 if (!graphics || (c >= 64 && c <= 95)) {
627 chr[pos] = g0_to_iso_table [c];
628 attr[pos] = *lastattr;
629 } else {
630 chr[pos] = c + (c >= 96 ? 64 : 96);
631 attr[pos] = *lastattr | VTX_G1;
632 }
633 } else {
634 c &= 0x7f;
635 chr[pos] = ((grhold && graphics ) ? *lastchr : ' ');
636 if (c <= 7) { /* Set alphanumerics-color */
637 attr[pos] = *lastattr;
638 next_attrib = (*lastattr & ~(VTX_COLMASK | VTX_HIDDEN)) + c;
639 nextattr = 1;
640 graphics = 0;
641 } else if (c == 8 || c == 9) { /* Flash on/off */
642 attr[pos] = (*lastattr & ~VTX_FLASH) + VTX_FLASH * (c == 8);
643 } else if (c == 10 || c == 11) { /* End/start box */
644 attr[pos] = (*lastattr & ~VTX_BOX) + VTX_BOX * (c == 11);
645 } else if (c == 12 || c == 13) { /* Normal/double height */
646 attr[pos] = (*lastattr & ~VTX_DOUBLE1) + VTX_DOUBLE1 * (c == 13);
647 if (c == 13)
648 doubleht = 1;
649 } else if (c == 14 || c == 15 || c == 27) { /* SO, SI, ESC (ignored) */
650 attr[pos] = *lastattr;
651 } else if (c >= 16 && c <= 23) { /* Set graphics-color */
652 attr[pos] = *lastattr;
653 next_attrib = (*lastattr & ~(VTX_COLMASK | VTX_HIDDEN)) + c - 16;
654 nextattr = 1;
655 graphics = 1;
656 } else if (c == 24) { /* Conceal display */
657 attr[pos] = *lastattr | VTX_HIDDEN;
658 } else if (c == 25 || c == 26) { /* Contiguous/separated graphics */
659 attr[pos] = (*lastattr & ~VTX_GRSEP) + VTX_GRSEP * (c == 26);
660 } else if (c == 28) { /* Black background */
661 attr[pos] = *lastattr & ~VTX_BGMASK;
662 } else if (c == 29) { /* Set background */
663 attr[pos] = (*lastattr & ~VTX_BGMASK) + ((*lastattr & VTX_COLMASK) << 3);
664 } else if (c == 30 || c == 31) { /* Hold/release graphics */
665 attr[pos] = *lastattr;
666 grhold = (c == 30);
667 if (grhold && graphics)
668 chr[pos] = *lastchr;
669 } else {
670 return 0;
671 }
672 }
673 lastchr = chr + pos;
674 if (nextattr) {
675 lastattr = &next_attrib;
676 nextattr = 0;
677 } else {
678 lastattr = attr + pos;
679 }
680 pos++;
681 }
682 }
683 }
684 return 1;
685 }
686
687 static SV *
688 decode_ansi (u8 *chr, u16 *atr)
689 {
690 UI x;
691 SV *sv = newSVpvn ("", 0);
692 u16 o;
693
694 for (x=0; x<VT_COLS; x++)
695 {
696 u16 a = *atr++;
697 if (x == 0 || (a&0x07) != (o&0x07))
698 sv_catpvf (sv, "\x1b[3%dm", a&7);
699 if (x == 0 || (a&0x38) != (o&0x38))
700 sv_catpvf (sv, "\x1b[4%dm", (o&0x38)>>3);
701 if (x == 0 || (a&VTX_FLASH) != (o&VTX_FLASH))
702 sv_catpvf (sv, "\x1b[%sm", a&VTX_FLASH ? "7" : "");
703
704 sv_catpvf (sv, "%c", a & VTX_G1 ? '×' : *chr);
705
706 chr++;
707 o = a;
708 }
709
710 sv_catpv (sv, "\x1b[37;40;0m");
711
712 return sv;
713 }
714
715 #define valid_packet(sv) (SvPOK(packet) && SvCUR(packet) == 40)
716 #define consume_byte(store) \
717 if (bp >= 39) \
718 { \
719 SV **sv = av_fetch(stream, pi, 0); pi++; \
720 if (!sv) \
721 goto eostream; \
722 packet = *sv; \
723 if (!valid_packet (pascket)) \
724 goto skip; \
725 p = SvPV_nolen (packet); \
726 bp = 0; \
727 } \
728 (store) = p[++bp]
729
730 static void
731 decode_stream (AV *stream)
732 {
733 dSP;
734
735 while (av_len (stream) >= 0)
736 {
737 UI pi = 1;
738 SV *packet = *av_fetch(stream, 0, 1);
739
740 if (valid_packet (packet))
741 {
742 u8 *p = SvPV_nolen(packet);
743 u8 bp = p[0] == 0xff ? p[1] : unham4 (p[0])*3+1;
744
745 if (bp <= 12*3+1 && p[bp] == 0xa1)
746 {
747 u8 buf[4];
748
749 consume_byte (buf[0]); consume_byte (buf[1]);
750 consume_byte (buf[2]); consume_byte (buf[3]);
751
752 {
753 u16 sh = unham8 (buf[0], buf[1]) | unham8 (buf[2], buf[3]) << 8;
754 u8 bt = sh & 0x1f;
755 u16 bl = sh >> 5;
756 SV *block = sv_2mortal (newSVpvn (&bt, 1));
757
758 while (bl--)
759 {
760 consume_byte (buf[0]);
761 sv_catpvn (block, buf, 1);
762 }
763
764 EXTEND (SP, 1);
765 PUSHs (block);
766
767 /* optimize, do only when still in first packet! */
768 do {
769 if (bp >= 39)
770 break;
771
772 bp++;
773 if (p[bp] == 0xa1)
774 {
775 p[0] = 0xff;
776 p[1] = bp;
777 pi--;
778 break;
779 }
780 } while (p[bp] = 0x5e);
781 }
782 }
783 }
784
785 skip:
786 while (pi--)
787 SvREFCNT_dec(av_shift(stream));
788 }
789 eostream:
790
791 PUTBACK;
792 }
793
794 static u8 *
795 unham_block (u8 *src, UI len, u8 *dst, UI dlen)
796 {
797 u16 sh = *src | (len-1)<<5;
798 u8 sum = ( sh & 15)
799 + ((sh >> 4) & 15)
800 + ((sh >> 8) & 15)
801 + ((sh >> 12) & 15);
802
803 if (len < 5)
804 return 0;
805
806 sum += unham8 (src[1], src[2]);
807 src += 3; len -= 3;
808
809 dlen--;
810
811 if (len < dlen)
812 return 0;
813
814 while (dlen)
815 {
816 *dst = unham8 (src[0], src[1]);
817 sum += (*dst >> 4) + (*dst & 15);
818 #if 0
819 printf ("%02x ", *dst);
820 #endif
821 dst++; src += 2; dlen--;
822 }
823 #if 0
824 printf ("\n");
825 printf ("sh = %04x, len = %02x, sum = %02x\n", sh, len, sum);
826 #endif
827 if (sum)
828 return 0;
829
830 return src;
831 }
832
833 /* must not be a macro, see nvec */
834 static U32
835 vec(u8 *d, UI bit, UI len)
836 {
837 U32 word = bit >> 3;
838
839 /*printf ("vec(%d,%d) %d (%d)\n",bit,len,(bit & 7) + len,(bit & 7) + len < 32);*/
840 assert ((bit & 7) + len < 32);
841
842 word = (d[word] )
843 | (d[word+1] << 8)
844 | (d[word+2] << 16)
845 | (d[word+3] << 24);
846
847 return (word >> (bit & 7)) & (((U32)1 << len) - 1);
848 }
849
850 #define hv_store_sv(name,value) hv_store (hv, #name, strlen(#name), (SV*)(value), 0)
851 #define hv_store_iv(name,value) hv_store_sv (name, newSViv (value))
852 #define nvec(bits) vec (c, (ofs += (bits)) - (bits), (bits))
853
854 #define decode_escape_sequences(av) \
855 { \
856 UI no_escapes = nvec (8); \
857 av = newAV (); \
858 while (no_escapes--) \
859 { \
860 AV *esc = newAV (); \
861 av_push (esc, newSViv (nvec (10))); \
862 av_push (esc, newSViv (nvec (6))); \
863 av_push (esc, newSViv (nvec (8))); \
864 av_push (av, newRV_noinc((SV*)esc)); \
865 } \
866 }
867
868 #define decode_transparent_string(sv, len) \
869 { \
870 UI l = len; \
871 sv = newSVpvn (s, l); \
872 s += l; \
873 while (l--) \
874 SvPV_nolen (sv)[l] &= 0x7f; \
875 }
876
877 #define decode_descriptor_loop(av, ll) \
878 av = newAV (); \
879 while (ll--) \
880 { \
881 AV *desc = newAV (); \
882 av_push (desc, newSViv (nvec (6))); \
883 av_push (desc, newSViv (nvec (6))); \
884 av_push (desc, newSViv (nvec (8))); \
885 av_push (av, newRV_noinc((SV*)desc)); \
886 }
887
888 static void
889 decode_epg (HV *hv, UI appid, u8 *c, u8 *s)
890 {
891 UI ofs = 16;
892 UI len;
893 AV *av;
894 SV *sv;
895
896 hv_store_iv (ca_mode, nvec(2));
897 hv_store_iv (_copyright, nvec(1));
898
899 ofs++; /* reserved */
900
901 switch (appid)
902 {
903 case 1:
904 {
905 hv_store_iv (epg_version, nvec( 6));
906 hv_store_iv (epg_version_swo, nvec( 6));
907 hv_store_iv (no_navigation, nvec(16));
908 hv_store_iv (no_osd, nvec(16));
909 hv_store_iv (no_message, nvec(16));
910 hv_store_iv (no_navigation_swo,nvec(16));
911 hv_store_iv (no_osd_swo, nvec(16));
912 hv_store_iv (no_message_swo, nvec(16));
913 len = nvec(8);
914 hv_store_iv (this_network_op, nvec( 8));
915 decode_transparent_string (sv, nvec (5));
916 hv_store_sv (service_name, sv);
917 hv_store_iv (no_updates, nvec( 1));
918
919 ofs += 2;
920
921 av = newAV ();
922 while (len--)
923 {
924 HV *hv = newHV ();
925 int LTO;
926 hv_store_iv (cni, nvec (16));
927 LTO = nvec (7) * 15;
928 if (nvec(1))
929 LTO = -LTO;
930 hv_store_iv (lto, LTO);
931 hv_store_iv (no_days, nvec( 5));
932 decode_transparent_string (sv, nvec( 5));
933 hv_store_sv (netwop_name, sv);
934 hv_store_iv (default_alphabet, nvec( 7));
935 hv_store_iv (prog_start_no, nvec(16));
936 hv_store_iv (prog_stop_no, nvec(16));
937 hv_store_iv (prog_stop_no_swo, nvec(16));
938 hv_store_iv (network_add_info, nvec(11));
939
940 av_push (av, newRV_noinc ((SV*)hv));
941 }
942 hv_store_sv (networks, newRV_noinc ((SV*)av));
943 break;
944 }
945 case 2:
946 {
947 UI background_reuse;
948
949 hv_store_iv (block_no, nvec(16));
950 hv_store_iv (audio_mode, vec(c, ofs, 12) & 3);
951 hv_store_iv (feature_flags, nvec(12));
952 hv_store_iv (netwop_no, nvec( 8));
953 hv_store_iv (start_time, nvec(16));
954 hv_store_iv (start_date, nvec(16));
955 hv_store_iv (stop_time, nvec(16));
956 hv_store_iv (_pil, nvec(20));
957 hv_store_iv (parental_rating, nvec( 4));
958 hv_store_iv (editorial_rating,nvec( 3));
959 {
960 UI no_themes = nvec( 3);
961 UI no_sortcrit = nvec( 3);
962 UI descriptor_looplength = nvec( 6);
963 background_reuse = nvec( 1);
964
965 av = newAV ();
966 while (no_themes--)
967 av_push (av, newSViv (nvec (8)));
968 hv_store_sv (themes, newRV_noinc((SV*)av));
969
970 av = newAV ();
971 while (no_sortcrit--)
972 av_push (av, newSViv (nvec (8)));
973 hv_store_sv (sortcrits, newRV_noinc((SV*)av));
974
975 decode_descriptor_loop (av, descriptor_looplength);
976 hv_store_sv (descriptors, newRV_noinc((SV*)av));
977
978 ofs = (ofs+7) & ~7;
979
980 decode_escape_sequences (av);
981 hv_store_sv (title_escape_sequences, newRV_noinc((SV*)av));
982 decode_transparent_string (sv, nvec (8));
983 hv_store_sv (title, sv);
984 }
985
986 if (background_reuse)
987 hv_store_iv (title_length, nvec (16));
988 else
989 {
990 decode_escape_sequences (av);
991 hv_store_sv (shortinfo_escape_sequences, newRV_noinc((SV*)av));
992 decode_transparent_string (sv, nvec (8));
993 hv_store_sv (shortinfo, sv);
994
995 len = nvec (3);
996 ofs += 2;
997 switch (len)
998 {
999 case 0:
1000 decode_escape_sequences (av);
1001 hv_store_sv (longinfo_escape_sequences, newRV_noinc((SV*)av));
1002 decode_transparent_string (sv, nvec (8));
1003 hv_store_sv (longinfo, sv);
1004 break;
1005 case 1:
1006 decode_escape_sequences (av);
1007 hv_store_sv (longinfo_escape_sequences, newRV_noinc((SV*)av));
1008 decode_transparent_string (sv, nvec (10));
1009 hv_store_sv (longinfo, sv);
1010 break;
1011 default:
1012 printf ("UNKNOWN LONGINFO TYPE %d\n", len);
1013 }
1014 }
1015
1016 break;
1017 }
1018 case 3:
1019 {
1020 UI descriptor_ll;
1021 hv_store_iv (block_no, nvec(16));
1022 hv_store_iv (header_size, nvec( 2));
1023 len = nvec(4);
1024 hv_store_iv (message_size, nvec( 3));
1025 ofs++;
1026 descriptor_ll = nvec(6);
1027 decode_descriptor_loop (av, descriptor_ll);
1028 hv_store_sv (descriptors, newRV_noinc((SV*)av));
1029 ofs = (ofs+7) & ~7;
1030
1031 decode_escape_sequences (av);
1032 hv_store_sv (header_escape_sequences, newRV_noinc((SV*)av));
1033
1034 decode_transparent_string (sv, nvec(8));
1035 hv_store_sv (header, sv);
1036
1037 hv_store_iv (message_attribute,nvec(8));
1038
1039 av = newAV ();
1040 while (len--)
1041 {
1042 AV *av2;
1043 UI len;
1044 HV *hv = newHV ();
1045
1046 hv_store_iv (next_id, nvec(16));
1047 hv_store_iv (next_type, nvec( 4));
1048
1049 len = nvec(4);
1050 av2 = newAV ();
1051 while (len--)
1052 {
1053 UI kind = nvec(8);
1054 av_push (av2, newSViv (kind));
1055 switch (kind)
1056 {
1057 case 0x02: case 0x10: case 0x11: case 0x18:
1058 case 0x20: case 0x21: case 0x22: case 0x23: case 0x24: case 0x25: case 0x26: case 0x27:
1059 case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: case 0x35: case 0x36: case 0x37:
1060 case 0x40: case 0x41:
1061 av_push (av2, newSViv (nvec ( 8)));
1062 break;
1063
1064 case 0x80: case 0x81:
1065 av_push (av2, newSViv (nvec (16)));
1066 break;
1067
1068 case 0xc0:
1069 av_push (av2, newSViv (nvec (12)));
1070 av_push (av2, newSViv (nvec (12)));
1071 break;
1072
1073 case 0xc8: case 0xc9:
1074 av_push (av2, newSViv (nvec (24)));
1075 break;
1076
1077 default:
1078 abort ();
1079 }
1080 }
1081 hv_store_sv (attributes, newRV_noinc((SV*)av2));
1082
1083 decode_escape_sequences (av2);
1084 hv_store_sv (header_escape_sequences, newRV_noinc((SV*)av2));
1085 decode_transparent_string (sv, nvec(8));
1086 hv_store_sv (event, sv);
1087
1088 av_push (av, newRV_noinc((SV*)hv));
1089 }
1090 hv_store_sv (events, newRV_noinc((SV*)av));
1091
1092 break;
1093 }
1094 case 4:
1095 {
1096 UI block_no = nvec (16);
1097 UI descriptor_ll;
1098 UI msg_len;
1099 hv_store_iv (block_no, block_no);
1100 hv_store_iv (message_attribute,nvec(8));
1101 hv_store_iv (header_size, nvec(3));
1102 hv_store_iv (message_size, nvec(3));
1103 descriptor_ll = nvec(6);
1104 if (!block_no)
1105 {
1106 decode_escape_sequences (av);
1107 hv_store_sv (message_escape_sequences, newRV_noinc((SV*)av));
1108 msg_len = nvec(10);
1109 ofs += 6;
1110 }
1111
1112 decode_escape_sequences (av);
1113 hv_store_sv (header_escape_sequences, newRV_noinc((SV*)av));
1114
1115 decode_transparent_string (sv, nvec(8));
1116 hv_store_sv (header, sv);
1117
1118 if (!block_no)
1119 {
1120 decode_transparent_string (sv, msg_len);
1121 hv_store_sv (message, sv);
1122 }
1123
1124 decode_descriptor_loop (av, descriptor_ll);
1125 hv_store_sv (descriptors, newRV_noinc((SV*)av));
1126 break;
1127 }
1128 case 5:
1129 {
1130 UI descriptor_ll;
1131 hv_store_iv (block_no, nvec(16));
1132 descriptor_ll = nvec(6);
1133 decode_descriptor_loop (av, descriptor_ll);
1134 hv_store_sv (descriptors, newRV_noinc((SV*)av));
1135 ofs = (ofs+7) & ~7;
1136
1137 decode_escape_sequences (av);
1138 hv_store_sv (message_escape_sequences, newRV_noinc((SV*)av));
1139
1140 decode_transparent_string (sv, nvec(10));
1141 hv_store_sv (message, sv);
1142
1143 break;
1144 }
1145 default:
1146 printf ("UNKNOWN EPG DATATYPE ($%02x)\n", appid);
1147 }
1148 }
1149
1150 static SV *
1151 decode_block(u8 *b, UI len, AV *bi)
1152 {
1153 dSP;
1154 u8 ctrl[1024];
1155 u8 bt = *b;
1156
1157 if (bt == 0)
1158 {
1159 if ((b = unham_block (b, len, ctrl, (len-1)>>1 )))
1160 {
1161 UI app_no = ctrl[0];
1162 UI app;
1163 av_clear (bi);
1164 for (app=1; app<=app_no; app++)
1165 av_store (bi, app, newSViv (vec (ctrl, app*16-8, 16)));
1166 }
1167 }
1168 else if (len >= 5)
1169 {
1170 if (bt <= av_len (bi) && SvOK(*av_fetch (bi, bt, 0)))
1171 {
1172 u16 appid = SvIV (*av_fetch (bi, bt, 0));
1173 if (appid == 0) /* EPG */
1174 {
1175 if ((b = unham_block (b, len, ctrl, unham8 (b[3], b[4]) | (unham4 (b[5]) << 8) & 1023)))
1176 {
1177 HV *hv = newHV ();
1178 /* _now_ we have an epg structure. decode it */
1179
1180 appid = vec(ctrl,10,6);
1181 EXTEND (SP, 2);
1182 PUSHs (sv_2mortal (newSViv (appid)));
1183 PUSHs (sv_2mortal (newRV_noinc ((SV*)hv)));
1184
1185 decode_epg (hv, appid, ctrl, b);
1186 }
1187 else
1188 printf ("checksum error found block %d, len %d, appid = %d (clen 0)\n", bt, len, appid);
1189 }
1190 else
1191 /* other applications not defined (to my knowledge) */;
1192 }
1193 else
1194 /* no bundle info: can't parse yet */;
1195 }
1196
1197 PUTBACK;
1198 }
1199
1200 MODULE = Video::Capture::VBI PACKAGE = Video::Capture::VBI
1201
1202 PROTOTYPES: ENABLE
1203
1204 int
1205 unham4(data)
1206 SV * data
1207 CODE:
1208 STRLEN len;
1209 unsigned char *d = (unsigned char *)SvPV (data, len);
1210
1211 if (len < 1)
1212 croak ("unham4: length must be at least 1");
1213
1214 RETVAL = unham4 (*d);
1215 OUTPUT:
1216 RETVAL
1217
1218 int
1219 unham8(data)
1220 SV * data
1221 CODE:
1222 STRLEN len;
1223 unsigned char *d = (unsigned char *)SvPV (data, len);
1224
1225 if (len < 2)
1226 croak ("unham8: length must be at least 2");
1227
1228 RETVAL = unham8 (d[0], d[1]);
1229 OUTPUT:
1230 RETVAL
1231
1232 void
1233 decode_field(field, types)
1234 SV * field
1235 unsigned int types
1236 PPCODE:
1237 UI lines = SvCUR(field) / VBI_BPL;
1238 UI line;
1239 decoder dec;
1240
1241 decoder_init (&dec, types);
1242
1243 EXTEND (SP, lines);
1244 for (line = 0; line < lines; line++)
1245 {
1246 SV *sv = decoder_decode (&dec, line, ((u8*)SvPV_nolen(field)) + line * VBI_BPL);
1247 if (sv)
1248 PUSHs (sv_2mortal (sv));
1249 }
1250
1251 void
1252 decode_vtpage(data)
1253 SV * data
1254 PPCODE:
1255 u8 chr[VT_COLS*VT_LINES];
1256 u16 atr[VT_COLS*VT_LINES];
1257 UI lines;
1258
1259 if (!SvPOK(data))
1260 XSRETURN_EMPTY;
1261
1262 lines = SvCUR(data) / VT_COLS;
1263
1264 if (lines > VT_LINES)
1265 croak ("videotext cannot have more than %d lines (argument has %d lines)\n", VT_LINES, lines);
1266
1267 Zero(chr, VT_COLS*VT_LINES, u8);
1268 Zero(atr, VT_COLS*VT_LINES, u16);
1269 if (decode_vtpage (SvPV_nolen(data), lines, chr, atr))
1270 {
1271 AV *av = newAV ();
1272 UI n;
1273
1274 for (n = 0; n < VT_COLS*lines; n++)
1275 av_push (av, newSViv (atr[n]));
1276
1277 EXTEND (SP, 2);
1278 PUSHs (sv_2mortal (newSVpvn (chr, VT_COLS*lines)));
1279 PUSHs (sv_2mortal (newRV_noinc ((SV*)av)));
1280 }
1281
1282 PROTOTYPES: DISABLE
1283
1284 void
1285 decode_ansi(chr, atr)
1286 SV * chr
1287 SV * atr
1288 PPCODE:
1289 UI lines = SvCUR(chr) / VT_COLS;
1290 UI attr_i = 0;
1291 u8 *_chr = SvPV_nolen (chr);
1292 u16 _atr[VT_COLS];
1293
1294 EXTEND (SP, lines);
1295
1296 while (lines--)
1297 {
1298 UI attr_j;
1299 for(attr_j = 0; attr_j < VT_COLS; attr_j++)
1300 _atr[attr_j] = SvIV (*av_fetch ((AV*)SvRV (atr), attr_i+attr_j, 1));
1301
1302 PUSHs (sv_2mortal (decode_ansi (_chr, _atr)));
1303
1304 _chr += VT_COLS;
1305 attr_i += VT_COLS;
1306 }
1307
1308 unsigned int
1309 bcd2dec(bcd)
1310 unsigned int bcd
1311 CODE:
1312 UI digit = 1;
1313 RETVAL = 0;
1314 while (bcd)
1315 {
1316 if ((bcd & 15) > 9)
1317 XSRETURN_EMPTY;
1318
1319 RETVAL += (bcd & 15) * digit;
1320 digit *= 10;
1321 bcd >>= 4;
1322 }
1323 OUTPUT:
1324 RETVAL
1325
1326 PROTOTYPES: ENABLE
1327
1328 MODULE = Video::Capture::VBI PACKAGE = Video::Capture::VBI::EPG
1329
1330 void
1331 decode_stream(stream)
1332 SV * stream
1333 PPCODE:
1334 if (!SvROK(stream) || SvTYPE(SvRV(stream)) != SVt_PVAV)
1335 croak ("decode_epg stream works on arrayrefs");
1336
1337 PUTBACK;
1338 decode_stream ((AV*)SvRV(stream));
1339 SPAGAIN;
1340
1341 SV *
1342 decode_block(block, bundle)
1343 SV * block
1344 SV * bundle
1345 PPCODE:
1346 if (!SvROK(bundle) || SvTYPE(SvRV(bundle)) != SVt_PVAV)
1347 croak ("bundle info must be arrayref");
1348
1349 PUTBACK;
1350 decode_block (SvPV_nolen(block), SvCUR(block), (AV*)SvRV(bundle));
1351 SPAGAIN;
1352
1353 BOOT:
1354 {
1355 HV *stash = gv_stashpvn("Video::Capture::VBI", 19, TRUE);
1356
1357 newCONSTSUB(stash,"VBI_VT", newSViv(VBI_VT));
1358 newCONSTSUB(stash,"VBI_VPS", newSViv(VBI_VPS));
1359 newCONSTSUB(stash,"VBI_VDAT", newSViv(VBI_VDAT));
1360 newCONSTSUB(stash,"VBI_VC", newSViv(VBI_VC));
1361 newCONSTSUB(stash,"VBI_EMPTY", newSViv(VBI_EMPTY));
1362 newCONSTSUB(stash,"VBI_OTHER", newSViv(VBI_OTHER));
1363
1364 newCONSTSUB(stash,"VTX_COLMASK",newSViv(VTX_COLMASK));
1365 newCONSTSUB(stash,"VTX_GRSEP", newSViv(VTX_GRSEP));
1366 newCONSTSUB(stash,"VTX_HIDDEN", newSViv(VTX_HIDDEN));
1367 newCONSTSUB(stash,"VTX_BOX", newSViv(VTX_BOX));
1368 newCONSTSUB(stash,"VTX_FLASH", newSViv(VTX_FLASH));
1369 newCONSTSUB(stash,"VTX_DOUBLE1",newSViv(VTX_DOUBLE1));
1370 newCONSTSUB(stash,"VTX_DOUBLE2",newSViv(VTX_DOUBLE2));
1371 newCONSTSUB(stash,"VTX_INVERT", newSViv(VTX_INVERT));
1372 newCONSTSUB(stash,"VTX_DOUBLE", newSViv(VTX_DOUBLE));
1373 }
1374