ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Video-Capture-V4l/VBI/VBI.xs
Revision: 1.5
Committed: Tue Nov 1 13:59:42 2005 UTC (18 years, 8 months ago) by root
Branch: MAIN
Changes since 1.4: +20 -6 lines
Log Message:
*** empty log message ***

File Contents

# User Rev Content
1 pcg 1.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 pcg 1.2 #define FREQ_VT FREQ_VT_PAL /* Replace by FREQ_VT_NTSC and pray that it works */
111 pcg 1.1 /*#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 root 1.5 decode_vps (u8 *data)
289 pcg 1.1 {
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 root 1.5 decode_vt (u8 *data)
312 pcg 1.1 {
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 root 1.5 decode_empty ()
419 pcg 1.1 {
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 root 1.4 ; /* no teletext page */
495 pcg 1.1 }
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 root 1.5 return decode_vt (data);
505 pcg 1.1 }
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 root 1.5 return decode_vps (data);
523 pcg 1.1 }
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 root 1.3 /* watch out for empty lines, test signals etc.. */
542 pcg 1.1 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 root 1.5 return decode_empty ();
557 pcg 1.1 }
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 root 1.4 " !\"#$%&'()*+,-./0123456789:;<=>?"
580     "@ABCDEFGHIJKLMNOPQRSTUVWXYZAOU^#"
581     "-abcdefghijklmnopqrstuvwxyzaous#"
582 pcg 1.1 " "
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 root 1.4 for (x=0; x < VT_COLS; x++)
695 pcg 1.1 {
696     u16 a = *atr++;
697 root 1.4 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 pcg 1.1
704 root 1.4 sv_catpvf (sv, "%c", a & VTX_G1 ? 'x' : *chr);
705 pcg 1.1
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 root 1.5 {
1238 pcg 1.1 UI lines = SvCUR(field) / VBI_BPL;
1239     UI line;
1240     decoder dec;
1241    
1242     decoder_init (&dec, types);
1243    
1244     EXTEND (SP, lines);
1245     for (line = 0; line < lines; line++)
1246     {
1247     SV *sv = decoder_decode (&dec, line, ((u8*)SvPV_nolen(field)) + line * VBI_BPL);
1248     if (sv)
1249     PUSHs (sv_2mortal (sv));
1250     }
1251 root 1.5 }
1252    
1253     SV *
1254     decode_vps(void *data)
1255    
1256     SV *
1257     decode_vt(void *data)
1258 pcg 1.1
1259     void
1260     decode_vtpage(data)
1261     SV * data
1262     PPCODE:
1263 root 1.5 {
1264 pcg 1.1 u8 chr[VT_COLS*VT_LINES];
1265     u16 atr[VT_COLS*VT_LINES];
1266     UI lines;
1267    
1268     if (!SvPOK(data))
1269     XSRETURN_EMPTY;
1270    
1271     lines = SvCUR(data) / VT_COLS;
1272    
1273     if (lines > VT_LINES)
1274     croak ("videotext cannot have more than %d lines (argument has %d lines)\n", VT_LINES, lines);
1275    
1276     Zero(chr, VT_COLS*VT_LINES, u8);
1277     Zero(atr, VT_COLS*VT_LINES, u16);
1278     if (decode_vtpage (SvPV_nolen(data), lines, chr, atr))
1279     {
1280     AV *av = newAV ();
1281     UI n;
1282    
1283     for (n = 0; n < VT_COLS*lines; n++)
1284     av_push (av, newSViv (atr[n]));
1285    
1286     EXTEND (SP, 2);
1287     PUSHs (sv_2mortal (newSVpvn (chr, VT_COLS*lines)));
1288     PUSHs (sv_2mortal (newRV_noinc ((SV*)av)));
1289     }
1290 root 1.5 }
1291 pcg 1.1
1292     PROTOTYPES: DISABLE
1293    
1294     void
1295     decode_ansi(chr, atr)
1296     SV * chr
1297     SV * atr
1298     PPCODE:
1299 root 1.5 {
1300 pcg 1.1 UI lines = SvCUR(chr) / VT_COLS;
1301     UI attr_i = 0;
1302     u8 *_chr = SvPV_nolen (chr);
1303     u16 _atr[VT_COLS];
1304    
1305     EXTEND (SP, lines);
1306    
1307     while (lines--)
1308     {
1309     UI attr_j;
1310     for(attr_j = 0; attr_j < VT_COLS; attr_j++)
1311     _atr[attr_j] = SvIV (*av_fetch ((AV*)SvRV (atr), attr_i+attr_j, 1));
1312    
1313     PUSHs (sv_2mortal (decode_ansi (_chr, _atr)));
1314    
1315     _chr += VT_COLS;
1316     attr_i += VT_COLS;
1317     }
1318 root 1.5 }
1319 pcg 1.1
1320     unsigned int
1321     bcd2dec(bcd)
1322     unsigned int bcd
1323     CODE:
1324 root 1.5 {
1325 pcg 1.1 UI digit = 1;
1326     RETVAL = 0;
1327     while (bcd)
1328     {
1329     if ((bcd & 15) > 9)
1330     XSRETURN_EMPTY;
1331    
1332     RETVAL += (bcd & 15) * digit;
1333     digit *= 10;
1334     bcd >>= 4;
1335     }
1336 root 1.5 }
1337 pcg 1.1 OUTPUT:
1338     RETVAL
1339    
1340     PROTOTYPES: ENABLE
1341    
1342     MODULE = Video::Capture::VBI PACKAGE = Video::Capture::VBI::EPG
1343    
1344     void
1345     decode_stream(stream)
1346     SV * stream
1347     PPCODE:
1348     if (!SvROK(stream) || SvTYPE(SvRV(stream)) != SVt_PVAV)
1349     croak ("decode_epg stream works on arrayrefs");
1350    
1351     PUTBACK;
1352     decode_stream ((AV*)SvRV(stream));
1353     SPAGAIN;
1354    
1355     SV *
1356     decode_block(block, bundle)
1357     SV * block
1358     SV * bundle
1359     PPCODE:
1360     if (!SvROK(bundle) || SvTYPE(SvRV(bundle)) != SVt_PVAV)
1361     croak ("bundle info must be arrayref");
1362    
1363     PUTBACK;
1364     decode_block (SvPV_nolen(block), SvCUR(block), (AV*)SvRV(bundle));
1365     SPAGAIN;
1366    
1367     BOOT:
1368     {
1369     HV *stash = gv_stashpvn("Video::Capture::VBI", 19, TRUE);
1370    
1371     newCONSTSUB(stash,"VBI_VT", newSViv(VBI_VT));
1372     newCONSTSUB(stash,"VBI_VPS", newSViv(VBI_VPS));
1373     newCONSTSUB(stash,"VBI_VDAT", newSViv(VBI_VDAT));
1374     newCONSTSUB(stash,"VBI_VC", newSViv(VBI_VC));
1375     newCONSTSUB(stash,"VBI_EMPTY", newSViv(VBI_EMPTY));
1376     newCONSTSUB(stash,"VBI_OTHER", newSViv(VBI_OTHER));
1377    
1378     newCONSTSUB(stash,"VTX_COLMASK",newSViv(VTX_COLMASK));
1379     newCONSTSUB(stash,"VTX_GRSEP", newSViv(VTX_GRSEP));
1380     newCONSTSUB(stash,"VTX_HIDDEN", newSViv(VTX_HIDDEN));
1381     newCONSTSUB(stash,"VTX_BOX", newSViv(VTX_BOX));
1382     newCONSTSUB(stash,"VTX_FLASH", newSViv(VTX_FLASH));
1383     newCONSTSUB(stash,"VTX_DOUBLE1",newSViv(VTX_DOUBLE1));
1384     newCONSTSUB(stash,"VTX_DOUBLE2",newSViv(VTX_DOUBLE2));
1385     newCONSTSUB(stash,"VTX_INVERT", newSViv(VTX_INVERT));
1386     newCONSTSUB(stash,"VTX_DOUBLE", newSViv(VTX_DOUBLE));
1387     }
1388