Merge branch 'bug_615_replace_rawfile' into m2ts_test
[vuplus_dvbapp] / lib / dvb / teletext.cpp
1 #include <lib/base/eerror.h>
2 #include <lib/dvb/teletext.h>
3 #include <lib/dvb/idemux.h>
4 #include <lib/gdi/gpixmap.h>
5
6 // G0 and G2 national option table
7 // see table 33 in ETSI EN 300 706
8 // use it with (triplet 1 bits 14-11)*(ctrl bits C12-14)
9
10 unsigned char NationalOptionSubsetsLookup[16*8] =
11 {
12         1, 4, 11, 5, 3, 8, 0, 9,
13         7, 4, 11, 5, 3, 1, 0, 1,
14         1, 4, 11, 5, 3, 8, 12, 1,
15         1, 1, 1, 1, 1, 10, 1, 9,
16         1, 4, 2, 6, 1, 1, 0, 1,
17         1, 1, 1, 1, 1, 1, 1, 1, // reserved
18         1, 1, 1, 1, 1, 1, 12, 1,
19         1, 1, 1, 1, 1, 1, 1, 1, // reserved
20         1, 1, 1, 1, 3, 1, 1, 1,
21         1, 1, 1, 1, 1, 1, 1, 1, // reserved
22         1, 1, 1, 1, 1, 1, 1, 1,
23         1, 1, 1, 1, 1, 1, 1, 1, // reserved
24         1, 1, 1, 1, 1, 1, 1, 1, // reserved
25         1, 1, 1, 1, 1, 1, 1, 1, // reserved
26         1, 1, 1, 1, 1, 1, 1, 1, // reserved
27         1, 1, 1, 1, 1, 1, 1, 1  // reserved
28 };
29
30 unsigned char NationalReplaceMap[128] =
31 {
32         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
33         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
34         0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
35         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
36         3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
37         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 5, 6, 7, 8,
38         9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
39         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 11, 12, 13, 0
40 };
41
42 // national option subsets (UTF8)
43 // see table 36 in ETSI EN 300 706
44
45 unsigned int NationalOptionSubsets[13*14] = {
46         0, 0x0023, 0xc5af, 0xc48d, 0xc5a3, 0xc5be, 0xc3bd, 0xc3ad, 0xc599, 0xc3a9, 0xc3a1, 0xc49b, 0xc3ba, 0xc5a1, // Slovak/Czech
47         0, 0xc2a3, 0x0024, 0x0040, 0xe28690, 0xc2bd, 0xe28692, 0xe28691, 0x0023, 0x002d, 0xc2bc, 0xc781, 0xc2be, 0xc3b7, // English
48         0, 0x0023, 0xc3b5, 0xc5A0, 0xc384, 0xc396, 0xc5bd, 0xc39c, 0xc395, 0xc5a1, 0xc3a4, 0xc3b6, 0xc5be, 0xc3bc, // Estonian
49         0, 0xc3a9, 0xc3af, 0xc3a0, 0xc3ab, 0xc3aa, 0xc3b9, 0xc3ae, 0x0023, 0xc3a8, 0xc3a2, 0xc3b4, 0xc3bb, 0xc3a7, // French
50         0, 0x0023, 0x0024, 0xc2a7, 0xc384, 0xc396, 0xc39c, 0x005e, 0x005f, 0xcb9a, 0xc3a4, 0xc3b6, 0xc3bc, 0xc39f, // German
51         0, 0xc2a3, 0x0024, 0xc3a9, 0xcb9a, 0xc3a7, 0xe28692, 0xe28691, 0x0023, 0xc3b9, 0xc3a0, 0xc3b2, 0xc3a8, 0xc3ac, // Italian
52         0, 0x0023, 0x0024, 0xc5a0, 0xc497, 0xc8a9, 0xc5bd, 0xc48d, 0xc5ab, 0xc5a1, 0xc485, 0xc5b3, 0xc5be, 0xc4af/*FIXMEE*/, // Lithuanian/Lettish
53         0, 0x0023, 0xc584, 0xc485, 0xc6b5, 0xc59a, 0xc581, 0xc487, 0xc3b3, 0xc499, 0xc5bc, 0xc59b, 0xc582, 0xc5ba, // Polish
54         0, 0xc3a7, 0x0024, 0xc2a1, 0xc3a1, 0xc3a9, 0xc3ad, 0xc3b3, 0xc3ba, 0xc2bf, 0xc3bc, 0xc3b1, 0xc3a8, 0xc3a0, // Spanish/Portuguese
55         0, 0x0023, 0xc2a4, 0xc5a2, 0xc382, 0xc59e, 0xc78d, 0xc38e, 0xc4b1, 0xc5a3, 0xc3a2, 0xc59f, 0xc78e, 0xc3ae, // Rumanian
56         0, 0x0023, 0xc38b, 0xc48c, 0xc486, 0xc5bd, 0xc490, 0xc5a0, 0xc3ab, 0xc48d, 0xc487, 0xc5be, 0xc491, 0xc5a1, // Slovenian/Serbian/Croation
57         0, 0x0023, 0xc2a4, 0xc389, 0xc384, 0xc396, 0xc385, 0xc39c, 0x005f, 0xc3a9, 0xc3a4, 0xc3b6, 0xc3a5, 0xc3bc, // Finnish/Hungarian/Swedish
58         0, 0xee8080/*FIXME*/, 0xc7a7, 0xc4b0, 0xc59e, 0xc396, 0xc387, 0xc39c, 0xc7a6, 0xc4b1, 0xc59f, 0xc3b6, 0xc3a7, 0xc3bc  // Turkish
59 };
60
61 unsigned short diacr_upper_cmap[26*15] = {
62         0xc380, 0xc381, 0xc382, 0xc383, 0xc480, 0xc482, 0x0000, 0xc384, 0x0000, 0xc385, 0x0000, 0x0000, 0x0000, 0xc484, 0xc482,
63         0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
64         0x0000, 0xc486, 0xc488, 0x0000, 0x0000, 0xc48c, 0xc48a, 0x0000, 0x0000, 0x0000, 0xc387, 0x0000, 0x0000, 0x0000, 0xc48c,
65         0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xc48e, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xc48e,
66         0xc388, 0xc389, 0xc38a, 0x0000, 0xc492, 0xc494, 0xc496, 0xc38b, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xc498, 0xc49a,
67         0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
68         0x0000, 0x0000, 0xc49c, 0x0000, 0x0000, 0xc49e, 0xc4a0, 0x0000, 0x0000, 0x0000, 0xc4a2, 0x0000, 0x0000, 0x0000, 0x0000,
69         0x0000, 0x0000, 0xc4a4, 0x0000, 0xc4a6, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
70         0xc38c, 0xc38d, 0xc38e, 0xc4a8, 0xc4aa, 0xc4ac, 0xc4b0, 0xc38f, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xc4ae, 0xc4ac,
71         0x0000, 0x0000, 0xc4b4, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
72         0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xc4b6, 0x0000, 0x0000, 0x0000, 0x0000,
73         0x0000, 0xc4b9, 0x0000, 0x0000, 0x0000, 0x0000, 0xc4bf, 0x0000, 0x0000, 0x0000, 0xc4bb, 0x0000, 0x0000, 0x0000, 0xc4bd,
74         0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
75         0x0000, 0xc583, 0x0000, 0xc391, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xc585, 0x0000, 0x0000, 0x0000, 0xc587,
76         0xc392, 0xc393, 0xc394, 0xc395, 0xc58c, 0xc58e, 0x0000, 0xc396, 0x0000, 0x0000, 0x0000, 0x0000, 0xc590, 0x0000, 0xc58e,
77         0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
78         0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
79         0x0000, 0xc594, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xc596, 0x0000, 0x0000, 0x0000, 0xc598,
80         0x0000, 0xc59a, 0xc59c, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xc59e, 0x0000, 0x0000, 0x0000, 0xc5a0,
81         0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xc5a2, 0x0000, 0x0000, 0x0000, 0xc5a4,
82         0xc399, 0xc39a, 0xc39b, 0xc5a8, 0xc5aa, 0xc5ac, 0x0000, 0xc39c, 0x0000, 0xc5ae, 0x0000, 0x0000, 0xc5b0, 0xc5b2, 0xc5ac,
83         0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
84         0x0000, 0x0000, 0xc5b4, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
85         0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
86         0x0000, 0xc39d, 0xc5b6, 0x0000, 0x0000, 0x0000, 0x0000, 0xc5b8, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
87         0x0000, 0xc5b9, 0x0000, 0x0000, 0x0000, 0x0000, 0xc5bb, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xc5bd
88 };
89
90 unsigned short diacr_lower_cmap[26*15] = {
91         0xc3a0, 0xc3a1, 0xc3a2, 0xc3a3, 0xc481, 0xc483, 0x0000, 0xc3a4, 0x0000, 0xc3a5, 0x0000, 0x0000, 0x0000, 0xc485, 0xc483,
92         0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
93         0x0000, 0xc487, 0xc489, 0x0000, 0x0000, 0xc48d, 0xc48b, 0x0000, 0x0000, 0x0000, 0xc3a7, 0x0000, 0x0000, 0x0000, 0xc48d,
94         0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xc48f, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xc48f,
95         0xc3a8, 0xc3a9, 0xc3aa, 0x0000, 0xc493, 0xc495, 0xc497, 0xc3ab, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xc499, 0xc49b,
96         0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
97         0x0000, 0x0000, 0xc49d, 0x0000, 0x0000, 0xc49f, 0xc4a1, 0x0000, 0x0000, 0x0000, 0xc4a3, 0x0000, 0x0000, 0x0000, 0x0000,
98         0x0000, 0x0000, 0xc4a5, 0x0000, 0xc4a7, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
99         0xc3ac, 0xc3ad, 0xc3ae, 0xc4a9, 0xc4ab, 0xc4ad, 0xc4b1, 0xc3af, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xc4af, 0xc4ad,
100         0x0000, 0x0000, 0xc4b5, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
101         0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xc4b7, 0x0000, 0x0000, 0x0000, 0x0000,
102         0x0000, 0xc4ba, 0x0000, 0x0000, 0x0000, 0x0000, 0xc580, 0x0000, 0x0000, 0x0000, 0xc4bc, 0x0000, 0x0000, 0x0000, 0xc4be,
103         0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
104         0x0000, 0xc584, 0x0000, 0xc3b1, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xc586, 0x0000, 0x0000, 0x0000, 0xc588,
105         0xc3b2, 0xc3b3, 0xc3b4, 0xc3b5, 0xc58d, 0xc58f, 0x0000, 0xc3b6, 0x0000, 0x0000, 0x0000, 0x0000, 0xc591, 0x0000, 0xc58f,
106         0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
107         0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
108         0x0000, 0xc595, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xc597, 0x0000, 0x0000, 0x0000, 0xc599,
109         0x0000, 0xc59b, 0xc59d, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xc59f, 0x0000, 0x0000, 0x0000, 0xc5a1,
110         0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xc5a3, 0x0000, 0x0000, 0x0000, 0xc5a5,
111         0xc3b9, 0xc3ba, 0xc3bb, 0xc5a9, 0xc5ab, 0xc5ad, 0x0000, 0xc3bc, 0x0000, 0xc5af, 0x0000, 0x0000, 0xc5b1, 0xc5b3, 0xc5ad,
112         0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
113         0x0000, 0x0000, 0xc5b5, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
114         0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
115         0x0000, 0xc3bd, 0xc5b7, 0x0000, 0x0000, 0x0000, 0x0000, 0xc3bf, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
116         0x0000, 0xc5ba, 0x0000, 0x0000, 0x0000, 0x0000, 0xc5bc, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xc5be
117 };
118
119 unsigned int Latin_G2_set[6*16] = {
120         0x0020, 0xc2a1, 0xc2a2, 0xc2a3, 0x0024, 0xc2a5, 0x0023, 0xc2a7, 0xc2a4, 0xc2b4, 0x0022, 0xc2ab, 0x003c, 0x005e, 0x003d, 0x0076,
121         0xc2b0, 0xc2b1, 0xc2b2, 0xc2b3, 0xc397, 0xc2b5, 0xc2b6, 0xc2b7, 0xc3b7, 0xc2b4, 0x0022, 0xc2bb, 0xc2bc, 0xc2bd, 0xc2be, 0xc2bf,
122         0x0020, 0x0060, 0xc2b4, 0xcb86, 0x007e, 0xcb89, 0xcb98, 0xcb99, 0xcc88, 0x002e, 0xcb9a, 0x0020, 0x005f, 0x0022, 0x0020, 0xcb98,
123         0x002d, 0xc2b9, 0xc2ae, 0xc2a9, 0xc4a2, 0x002a, 0xc2ac, 0xc0b0, 0xceb1, 0x0020, 0x0020, 0x0020, 0x002a, 0x002a, 0x002a, 0x002a,
124         0xcea9, 0xc386, 0xc490, 0x0061, 0xc4a6, 0x0020, 0xc4b2, 0xc4bf, 0xc581, 0xc398, 0xc592, 0x006f, 0xc39e, 0xc5a6, 0xc58a, 0xc589,
125         0xc4b8, 0xc3a6, 0xc491, 0xc48f, 0xc4a7, 0xc4b1, 0xc4b3, 0xc580, 0xc582, 0xc3b8, 0xc593, 0xc39f, 0xc3be, 0xc5a7, 0xc58b, 0x0020,
126 };
127
128 // This is a very simple en300 706 telext decoder.
129 // It can only decode a single page at a time, thus it's only used
130 // for subtitles. And it ONLY support LATIN Charsets yet!
131  
132 DEFINE_REF(eDVBTeletextParser);
133
134         /* we asumme error free transmission! */
135 static inline unsigned char decode_odd_parity(unsigned char *b)
136 {
137         int i;
138         unsigned char res = 0;
139         for (i=0; i<7; ++i)
140                 if (*b & (0x80 >> i))
141                         res |= 1<<i;
142         return res;
143 }
144
145 static inline unsigned char decode_hamming_84(unsigned char *b)
146 {
147         return ((*b << 3) & 8) | ((*b     ) & 4) | ((*b >> 3) & 2) | ((*b >> 6) & 1);
148 }
149
150 static inline unsigned long decode_hamming_2418(unsigned char *b)
151 {
152         static const unsigned char rev[16] = {
153                 0x00,0x08,0x04,0x0c,
154                 0x02,0x0a,0x06,0x0e,
155                 0x01,0x09,0x05,0x0d,
156                 0x03,0x0b,0x07,0x0f
157         };
158         b[0] = rev[b[0] >> 4] | (rev[b[0] & 0xf] << 4);
159         b[1] = rev[b[1] >> 4] | (rev[b[1] & 0xf] << 4);
160         b[2] = rev[b[2] >> 4] | (rev[b[2] & 0xf] << 4);
161         return ((b[0] & 0x04) >> 2) | ((b[0] & 0x70) >> 3) | ((b[1] & 0x7f) << 4) | ((b[2] & 0x7f) << 11);
162 }
163
164 static int extractPTS(pts_t &pts, unsigned char *pkt)
165 {
166         pkt += 7;
167         int flags = *pkt++;
168         
169         pkt++; // header length
170         
171         if (flags & 0x80) /* PTS present? */
172         {
173                         /* damn gcc bug */
174                 pts  = ((unsigned long long)(((pkt[0] >> 1) & 7))) << 30;
175                 pts |=   pkt[1] << 22;
176                 pts |=  (pkt[2]>>1) << 15;
177                 pts |=   pkt[3] << 7;
178                 pts |=  (pkt[5]>>1);
179                 
180                 return 0;
181         } else
182                 return -1;
183 }
184
185 eDVBTeletextParser::eDVBTeletextParser(iDVBDemux *demux)
186 {
187         setStreamID(0xBD); /* as per en 300 472 */
188         
189         setPageAndMagazine(-1, -1);
190         
191         if (demux->createPESReader(eApp, m_pes_reader))
192                 eDebug("failed to create teletext subtitle PES reader!");
193         else
194                 m_pes_reader->connectRead(slot(*this, &eDVBTeletextParser::processData), m_read_connection);
195 }
196
197 eDVBTeletextParser::~eDVBTeletextParser()
198 {
199 }
200
201 char *get_bits(int val, int count)
202 {
203         static char buf[33];
204         memset(buf, 0, 32);
205         if (count < 33)
206                 for (int i=0; i < count; ++i)
207                 {
208                         buf[(count-i)-1]=val&1?'1':'0';
209                         val>>=1;
210                 }
211         return buf;
212 }
213
214 void eDVBTeletextParser::processPESPacket(__u8 *pkt, int len)
215 {
216         unsigned char *p = pkt;
217         
218         pts_t pts;
219         int have_pts = extractPTS(pts, pkt);
220         
221         p += 4; len -= 4; /* start code, already be verified by pes parser */
222         p += 2; len -= 2; /* length, better use the argument */ 
223         
224         p += 3; len -= 3; /* pes header */
225         
226         p += 0x24; len -= 0x24; /* skip header */
227         
228 //      eDebug("data identifier: %02x", *p);
229         
230         p++; len--;
231         
232         while (len > 2)
233         {
234                 /*unsigned char data_unit_id = */*p++;
235                 unsigned char data_unit_length = *p++;
236                 len -= 2;
237                 
238                 if (len < data_unit_length)
239                 {
240                         eDebug("data_unit_length > len");
241                         break;
242                 }
243                 
244                 if (data_unit_length != 44)
245                 {
246                         /* eDebug("illegal data unit length %d", data_unit_length); */
247                         break;
248                 }
249                 
250 //              if (data_unit_id != 0x03)
251 //              {
252 //                      /* eDebug("non subtitle data unit id %d", data_unit_id); */
253 //                      break;
254 //              }
255                 
256                 /*unsigned char line_offset =*/ *p++; len--;
257                 unsigned char framing_code = *p++; len--;
258
259                 int magazine_and_packet_address = decode_hamming_84(p++); len--;
260                 magazine_and_packet_address |= decode_hamming_84(p++)<<4; len--;
261
262                 unsigned char *data = p; p += 40; len -= 40;
263                 
264                 if (framing_code != 0xe4) /* no teletxt data */
265                         continue;
266
267                 int M = magazine_and_packet_address & 7,
268                         Y = magazine_and_packet_address >> 3;
269 //                      eDebug("line %d, framing code: %02x, M=%02x, Y=%02x", line_offset, framing_code, m_M, m_Y);
270
271                 if (Y == 0) /* page header */
272                 {
273                         int X = decode_hamming_84(data + 1) * 0x10 + decode_hamming_84(data),
274 //                              S1 = decode_hamming_84(data + 2),
275                                 S2C4 = decode_hamming_84(data + 3),
276 //                              S2 = S2C4 & 7,
277 //                              S3 = decode_hamming_84(data + 4),
278                                 S4C5C6 = decode_hamming_84(data + 5),
279 //                              S4 = S4C5C6 & 3,
280                                 C = ((S2C4 & 8) ? (1<<4) : 0) |
281                                         ((S4C5C6 & 0xC) << 3) |
282                                         (decode_hamming_84(data + 6) << 7) |
283                                         (decode_hamming_84(data + 7) << 11),
284                                 serial_mode = C & (1<<11);
285
286                                 /* page on the same magazine? end current page. */
287                         if ((serial_mode || M == m_page_M) && m_page_open)
288                         {
289                                 eDebug("Page End %d %lld", !have_pts, pts);
290                                 handlePageEnd(!have_pts, pts);
291                                 m_page_open = 0;
292                         }
293
294                         if ((C & (1<<6)) && (X != 0xFF) && !(C & (1<<5))) /* scan for pages with subtitle bit set */
295                         {
296                                 eDVBServicePMTHandler::subtitleStream s;
297                                 s.pid = m_pid;
298                                 s.subtitling_type = 0x01; // ebu teletext subtitle
299                                 s.teletext_page_number = X & 0xFF;
300                                 s.teletext_magazine_number = M & 7;
301                                 m_found_subtitle_pages.insert(s);
302                         }
303
304                                 /* correct page on correct magazine? open page. */
305                         if (M == m_page_M && X == m_page_X)
306                         {
307                                 eDebug("Page Start %d %lld", !have_pts, pts);
308                                 m_C = C;
309                                 m_Y = Y;
310                                 handlePageStart();
311                                 m_page_open = 1;
312                                 m_box_open = 0;
313                                 handleLine(data + 8, 32);
314                         }
315                 } else if (Y < 26) // directly displayable packet
316                 {
317                         /* data for the selected page ? */
318                         if (M == m_page_M && m_page_open)
319                         {
320                                 m_Y = Y;
321                                 m_box_open = 0;
322                                 handleLine(data, 40);
323                         }
324                 } else if (Y == 26 && m_page_open && M == m_page_M)
325                 {
326 //                      int designation_code = decode_hamming_84(data);
327                         int display_row=-1, display_column=-1;
328                         for (int a = 1; a < 40; a+=3)
329                         {
330                                 int val;
331                                 if ((val=decode_hamming_2418(data+a)) >= 0)
332                                 {
333                                         unsigned char addr = val & 0x3F;
334                                         unsigned char mode = (val >> 6) & 0x1F;
335                                         unsigned char data = (val >> 11) & 0x7F;
336                                         if (addr == 0x3f && mode == 0x1f) // termination marker
337                                                 break;
338                                         if (addr >= 40)
339                                         {
340                                                 if (mode == 4)
341                                                 {
342                                                         display_row = addr - 40;
343                                                         continue;
344                                                 }
345                                                 else
346                                                         eDebugNoNewLine("ignore unimplemented: ");
347                                         }
348                                         else //0..39 means column 0..39
349                                         {
350                                                 if (display_row != -1)
351                                                 {
352                                                         display_column = addr;
353 //                                                      eDebugNoNewLine("PosX(%d) ", display_column);
354 //                                                      eDebugNoNewLine("PosY(%d) ", display_row);
355                                                         if (mode > 15) //char from G0 set w/ diacr.
356                                                         {
357                                                                 unsigned int ch=data;
358                                                                 if (!mode&0xF)
359                                                                 {
360                                                                         if (data == 0x2A)
361                                                                                 ch = '@';
362                                                                 }
363                                                                 else
364                                                                 {
365                                                                         if (ch > 96 && ch < 123)
366                                                                                 ch = diacr_lower_cmap[(ch-97)*15+(mode&0xF)-1];
367                                                                         else if (ch > 64 && ch < 91)
368                                                                                 ch = diacr_upper_cmap[(ch-65)*15+(mode&0xF)-1];
369                                                                 }
370                                                                 if (ch)
371                                                                         m_modifications[(display_row<<16)|display_column] = ch ? ch : data;
372                                                                 else /* when data is 0 we set the diacr. mark later on the existing character ..
373                                                                                 this isn't described in the EN300706.. but i have seen this on "Das Erste" */
374                                                                         m_modifications[(display_row<<16)|display_column] = (mode&0xF);
375 //                                                              eDebug("char(%04x) w/ diacr. mark", ch);
376                                                                 continue;
377                                                         }
378                                                         else if (mode == 15) // char from G2 set
379                                                         {
380                                                                 if (data > 0x19)
381                                                                 {
382                                                                         unsigned int ch=Latin_G2_set[data-0x20];
383                                                                         m_modifications[(display_row<<16)|display_column] = ch;
384 //                                                                      eDebug("char(%04x) from G2 set", ch);
385                                                                         continue;
386                                                                 }
387                                                                 else
388                                                                         eDebugNoNewLine("ignore G2 char < 0x20: ");
389                                                         }
390                                                         else
391                                                                 eDebugNoNewLine("ignore unimplemented: ");
392                                                 }
393                                                 else
394                                                         eDebugNoNewLine("row is not selected.. ignore: ");
395                                         }
396                                         eDebugNoNewLine("triplet = %08x(%s) ", val, get_bits(val, 18));
397                                         eDebugNoNewLine("address = %02x(%s) ", addr, get_bits(addr, 6));
398                                         eDebugNoNewLine("mode = %02x(%s) ", mode, get_bits(mode, 5));
399                                         eDebug("data = %02x(%s)", data, get_bits(data, 7));
400                                 }
401                         }
402                 } else if (Y > 29)
403                         /*eDebug("non handled packet 30, 31", Y, decode_hamming_84(data))*/;
404                 else if (Y == 29 && M == m_page_M)
405                 {
406                         int designation_code = decode_hamming_84(data++);
407                         if (designation_code == 0) // 29/0
408                         {
409                                 m_M29_t1 = decode_hamming_2418(data);
410                                 m_M29_t2 = decode_hamming_2418(data+3);
411                                 if ((m_M29_t1 & 0xF) == 0) // format1
412                                         m_M29_0_valid = 1;
413                                 else
414                                         eDebug("non handled packet M/%d/0 format %d", Y, m_M29_t1 & 0xF);
415                         }
416                         else
417                                 eDebug("non handled packet M/%d/%d", Y, designation_code);
418                 }
419                 else if (m_page_open && M == m_page_M)
420                 {
421                         int designation_code = decode_hamming_84(data++);
422                         if (Y == 28 && designation_code == 0)   // 28/0
423                         {
424 #if 1
425                                 m_X28_t1 = decode_hamming_2418(data);
426                                 m_X28_t2 = decode_hamming_2418(data+3);
427                                 if ((m_X28_t1 & 0xF) == 0) // format1
428                                         m_X28_0_valid = 1;
429                                 else
430                                         eDebug("non handled packet X/%d/0 format %d", Y, m_X28_t1 & 0xF);
431 #else
432                                         int i=0;
433                                         for (; i < 39; i+=3)
434                                         {
435                                                 int tripletX = decode_hamming_2418(data+i);
436                                                 if (tripletX >= 0)
437                                                 {
438                                                         if (i == 0)
439                                                         {
440                                                                 if ((m_X28_t1 & 0xF) == 0) // format1
441                                                                         m_X28_0_valid = 1;
442                                                                 m_X28_t1 = tripletX;
443                                                         }
444                                                         else if (i == 1)
445                                                                 m_X28_t2 = tripletX;
446
447                                                         char *c = get_bits(tripletX, 18);
448                                                         int x=0;
449                                                         for (; x < 18; ++x)
450                                                                 eDebugNoNewLine("%c", c[x]);
451                                                         eDebug("");
452                                                 }
453                                                 else
454                                                         eDebug("decode_hamming_2418 failed!\n");
455                                                 data += 3;
456                                         }
457 #endif
458                         }
459                         else
460                                 eDebug("non handled packet X/%d/%d", Y, designation_code);
461                 }
462         }
463 }
464
465 int eDVBTeletextParser::start(int pid)
466 {
467         m_page_open = 0;
468
469         if (m_pes_reader)
470         {
471                 m_pid = pid;
472                 return m_pes_reader->start(pid);
473         }
474         else
475                 return -1;
476 }
477
478 void eDVBTeletextParser::handlePageStart()
479 {
480         if (m_C & (1<<4)) /* erase flag set */
481         {
482                 m_subtitle_page.clear();
483                 m_modifications.clear();
484                 m_X28_0_valid = 0;
485 //              eDebug("erase page!");
486         }
487 //      else
488 //              eDebug("no erase flag set!");
489         m_current_source_line = -1;
490 }
491
492 void eDVBTeletextParser::handleLine(unsigned char *data, int len)
493 {
494 /* // hexdump
495         for (int i=0; i<len; ++i)
496                 eDebugNoNewLine("%02x ", decode_odd_parity(data + i));
497         eDebug(""); */
498         
499         m_subtitle_page.clearLine(m_Y);
500
501         if (!m_Y) /* first line is page header, we don't need that. */
502         {
503                 m_double_height = -1;
504                 return;
505         }
506                 
507         if (m_double_height == m_Y)
508         {
509                 m_double_height = -1;
510                 return;
511         }
512
513         int last_was_white = 1, color = 1; /* start with whitespace. start with color=white. (that's unrelated.) */
514
515         static unsigned char out[128];
516
517         int outidx = 0,
518                 Gtriplet = 0,
519                 nat_opts = (m_C & (1<<14) ? 1 : 0) |
520                                         (m_C & (1<<13) ? 2 : 0) |
521                                         (m_C & (1<<12) ? 4 : 0),
522                 nat_subset_2 = NationalOptionSubsetsLookup[Gtriplet*8+nat_opts],
523                 nat_subset = nat_subset_2,
524                 second_G0_set = 0;
525
526         if (m_X28_0_valid)
527         {
528                 nat_subset = NationalOptionSubsetsLookup[(m_X28_t1 >> 7) & 0x7F];
529                 nat_subset_2 = NationalOptionSubsetsLookup[((m_X28_t1 >> 14) & 0xF) | ((m_X28_t2 & 7) << 4)];
530 //              eDebug("X/28/0 nat_subset %d, nat_subset2 %d", nat_subset, nat_subset_2);
531         }
532         else if (m_M29_0_valid)
533         {
534                 nat_subset = NationalOptionSubsetsLookup[(m_M29_t1 >> 7) & 0x7F];
535                 nat_subset_2 = NationalOptionSubsetsLookup[((m_M29_t1 >> 14) & 0xF) | ((m_M29_t2 & 7) << 4)];
536 //              eDebug("M/29/0 nat_subset %d, nat_subset2 %d", nat_subset, nat_subset_2);
537         }
538 /*      else
539                 eDebug("nat_opts = %d, nat_subset = %d, C121314 = %d%d%d",
540                         nat_opts, nat_subset,
541                         (m_C & (1<<12))?1:0,
542                         (m_C & (1<<13))?1:0,
543                         (m_C & (1<<14))?1:0);*/
544
545 //      eDebug("handle subtitle line: %d len", len);
546         for (int i=0; i<len; ++i)
547         {
548                 unsigned char b = decode_odd_parity(data + i);
549                 std::map<int,unsigned int>::iterator it = m_modifications.find((m_Y<<16)|i);
550
551                 if (it != m_modifications.end())
552                 {
553                         unsigned int utf8_code = it->second;
554 //                      eDebugNoNewLine("%c[%d]", b, b);
555                         if (utf8_code < 0x10)
556                         {
557                                 int mode = utf8_code;
558                                 if (b > 96 && b < 123)
559                                         utf8_code = diacr_lower_cmap[(b-97)*15+mode-1];
560                                 else if (b > 64 && b < 91)
561                                         utf8_code = diacr_upper_cmap[(b-65)*15+mode-1];
562                         }
563                         if (utf8_code > 0xFFFFFF)
564                                 out[outidx++]=(utf8_code&0xFF000000)>>24;
565                         if (utf8_code > 0xFFFF)
566                                 out[outidx++]=(utf8_code&0xFF0000)>>16;
567                         if (utf8_code > 0xFF)
568                                 out[outidx++]=(utf8_code&0xFF00)>>8;
569                         if (utf8_code)
570                                 out[outidx++]=utf8_code&0xFF;
571                         m_modifications.erase(it);
572                         continue;
573                 }
574
575                 if (b < 0x10) /* spacing attribute */
576                 {
577                         if (b < 8) /* colors */
578                         {
579                                 if (b != color) /* new color is split into a new string */
580                                 {
581                                         addSubtitleString(color, std::string((const char*)out, outidx), m_Y);
582                                         outidx = 0;
583                                         color = b;
584                                 }
585                         }
586                         else if (b == 0xd)
587                                 m_double_height = m_Y + 1;
588                         else if (b == 0xa)  // close box
589                                 m_box_open=0;
590                         else if (b == 0xb)  // open box
591                                 ++m_box_open;
592                         else
593                                 eDebug("[ignore %x]", b);
594                                 /* ignore other attributes */
595                 } else if (m_box_open>1)
596                 {
597 //                      eDebugNoNewLine("%c(%d)", b, b);
598                                 /* no more than one whitespace, only printable chars */
599                         if (((!last_was_white) || (b != ' ')) && (b >= 0x20))
600                         {
601                                 int cur_nat_subset = second_G0_set ? nat_subset_2 : nat_subset;
602
603                                 unsigned char offs = NationalReplaceMap[b];
604                                 if (offs)
605                                 {
606                                         unsigned int utf8_code =
607                                                 NationalOptionSubsets[cur_nat_subset*14+offs];
608                                         if (utf8_code > 0xFFFFFF)
609                                                 out[outidx++]=(utf8_code&0xFF000000)>>24;
610                                         if (utf8_code > 0xFFFF)
611                                                 out[outidx++]=(utf8_code&0xFF0000)>>16;
612                                         if (utf8_code > 0xFF)
613                                                 out[outidx++]=(utf8_code&0xFF00)>>8;
614                                         out[outidx++]=utf8_code&0xFF;
615                                 }
616                                 else
617                                         out[outidx++] = b;
618                                 last_was_white = b == ' ';
619                         }
620                         else if (b == 0x1b) // ESC ... switch between default G0 and second G0 charset
621                                 second_G0_set ^= 1;
622                 }
623         }
624 //      eDebug("");
625         addSubtitleString(color, std::string((const char*)out, outidx), m_Y);
626 }
627
628 void eDVBTeletextParser::handlePageEnd(int have_pts, const pts_t &pts)
629 {
630 //      eDebug("handle page end");
631         addSubtitleString(-2, "", -1); /* end last line */ 
632         
633         m_subtitle_page.m_have_pts = have_pts;
634         m_subtitle_page.m_pts = pts;
635         m_subtitle_page.m_timeout = 90000 * 20; /* 20s */
636         if (m_page_X != 0)
637                 sendSubtitlePage();  /* send assembled subtitle page to display */
638 }
639
640 void eDVBTeletextParser::setPageAndMagazine(int page, int magazine)
641 {
642         if (page > 0)
643                 eDebug("enable teletext subtitle page %x%02x", magazine, page);
644         else
645                 eDebug("disable teletext subtitles");
646         m_M29_0_valid = 0;
647         m_page_M = magazine; /* magazine to look for */
648         if (magazine != -1)
649                 m_page_M &= 7;
650         m_page_X = page;  /* page number */
651         if (page != -1)
652                 m_page_X &= 0xFF;
653 }
654
655 void eDVBTeletextParser::connectNewPage(const Slot1<void, const eDVBTeletextSubtitlePage&> &slot, ePtr<eConnection> &connection)
656 {
657         connection = new eConnection(this, m_new_subtitle_page.connect(slot));
658 }
659
660 void eDVBTeletextParser::addSubtitleString(int color, std::string string, int source_line)
661 {
662 //      eDebug("(%d)add subtitle string: %s, col %d", m_Y, string.c_str(), color);
663         int force_cell = 0;
664
665         if (string.substr(0, 2) == "- ")
666         {
667                 string = string.substr(2);
668                 force_cell = 1;
669         }
670
671 //      eDebug("color %d, m_subtitle_color %d", color, m_subtitle_color);
672         if ((color != m_subtitle_color || force_cell) && !m_subtitle_text.empty() && ((color == -2) || !string.empty()))
673         {
674                 const gRGB pal[4] = { gRGB(64, 0, 76), gRGB(180, 40, 86), gRGB(160, 170, 105), gRGB(250, 200, 140) };
675 //              eDebug("add text |%s|: %d != %d || %d", m_subtitle_text.c_str(), color, m_subtitle_color, force_cell);
676                 m_subtitle_page.m_elements.push_back(eDVBTeletextSubtitlePageElement(pal[m_subtitle_color & 3], m_subtitle_text, m_current_source_line));
677                 m_current_source_line = -1;
678                 m_subtitle_text = "";
679         } else if (!m_subtitle_text.empty() && m_subtitle_text[m_subtitle_text.size()-1] != ' ')
680                 m_subtitle_text += " ";
681
682         if (m_current_source_line == -1)
683                 m_current_source_line = source_line;
684
685         if (!string.empty())
686         {
687 //              eDebug("set %d as new color", color);
688                 if (color >= 0)
689                         m_subtitle_color = color;
690                 m_subtitle_text += string;
691         }
692 }
693
694 void eDVBTeletextParser::sendSubtitlePage()
695 {
696 //      eDebug("subtitle page:");
697         bool send=m_C & (1<<4);
698         for (unsigned int i = 0; i < m_subtitle_page.m_elements.size(); ++i)
699                 if (!m_subtitle_page.m_elements[i].m_text.empty())
700                         send=true;
701         if (send)
702                 m_new_subtitle_page(m_subtitle_page);
703 }