add Misc Source/Converter
[enigma2.git] / 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.
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         return ((b[0] & 0x04) >> 2) | ((b[0] & 0x70) >> 3) | ((b[1] & 0x7f) << 4) | ((b[2] & 0x7f) << 11);
153 }
154
155 static int extractPTS(pts_t &pts, unsigned char *pkt)
156 {
157         pkt += 7;
158         int flags = *pkt++;
159         
160         pkt++; // header length
161         
162         if (flags & 0x80) /* PTS present? */
163         {
164                         /* damn gcc bug */
165                 pts  = ((unsigned long long)(((pkt[0] >> 1) & 7))) << 30;
166                 pts |=   pkt[1] << 22;
167                 pts |=  (pkt[2]>>1) << 15;
168                 pts |=   pkt[3] << 7;
169                 pts |=  (pkt[5]>>1);
170                 
171                 return 0;
172         } else
173                 return -1;
174 }
175
176 eDVBTeletextParser::eDVBTeletextParser(iDVBDemux *demux)
177 {
178         setStreamID(0xBD); /* as per en 300 472 */
179         
180         setPageAndMagazine(-1, -1);
181         
182         if (demux->createPESReader(eApp, m_pes_reader))
183                 eDebug("failed to create teletext subtitle PES reader!");
184         else
185                 m_pes_reader->connectRead(slot(*this, &eDVBTeletextParser::processData), m_read_connection);
186 }
187
188 eDVBTeletextParser::~eDVBTeletextParser()
189 {
190 }
191
192 char *get_bits(int val, int count)
193 {
194         static char buf[33];
195         memset(buf, 0, 32);
196         if (count < 33)
197                 for (int i=0; i < count; ++i)
198                 {
199                         buf[(count-i)-1]=val&1?'1':'0';
200                         val>>=1;
201                 }
202         return buf;
203 }
204
205 void eDVBTeletextParser::processPESPacket(__u8 *pkt, int len)
206 {
207         unsigned char *p = pkt;
208         
209         pts_t pts;
210         int have_pts = extractPTS(pts, pkt);
211         
212         p += 4; len -= 4; /* start code, already be verified by pes parser */
213         p += 2; len -= 2; /* length, better use the argument */ 
214         
215         p += 3; len -= 3; /* pes header */
216         
217         p += 0x24; len -= 0x24; /* skip header */
218         
219 //      eDebug("data identifier: %02x", *p);
220         
221         p++; len--;
222         
223         while (len > 2)
224         {
225                 /*unsigned char data_unit_id = */*p++;
226                 unsigned char data_unit_length = *p++;
227                 len -= 2;
228                 
229                 if (len < data_unit_length)
230                 {
231                         eDebug("data_unit_length > len");
232                         break;
233                 }
234                 
235                 if (data_unit_length != 44)
236                 {
237                         /* eDebug("illegal data unit length %d", data_unit_length); */
238                         break;
239                 }
240                 
241 //              if (data_unit_id != 0x03)
242 //              {
243 //                      /* eDebug("non subtitle data unit id %d", data_unit_id); */
244 //                      break;
245 //              }
246                 
247                 /*unsigned char line_offset =*/ *p++; len--;
248                 unsigned char framing_code = *p++; len--;
249
250                 int magazine_and_packet_address = decode_hamming_84(p++); len--;
251                 magazine_and_packet_address |= decode_hamming_84(p++)<<4; len--;
252
253                 unsigned char *data = p; p += 40; len -= 40;
254                 
255                 if (framing_code != 0xe4) /* no teletxt data */
256                         continue;
257
258                 int M = magazine_and_packet_address & 7,
259                         Y = magazine_and_packet_address >> 3;
260 //                      eDebug("line %d, framing code: %02x, M=%02x, Y=%02x", line_offset, framing_code, m_M, m_Y);
261                 
262                 if (Y == 0) /* page header */
263                 {
264                         int X = decode_hamming_84(data + 1) * 0x10 + decode_hamming_84(data),
265 //                              S1 = decode_hamming_84(data + 2),
266                                 S2C4 = decode_hamming_84(data + 3),
267 //                              S2 = S2C4 & 7,
268 //                              S3 = decode_hamming_84(data + 4),
269                                 S4C5C6 = decode_hamming_84(data + 5),
270 //                              S4 = S4C5C6 & 3,
271                                 C = ((S2C4 & 8) ? (1<<4) : 0) |
272                                         ((S4C5C6 & 0xC) << 3) |
273                                         (decode_hamming_84(data + 6) << 7) |
274                                         (decode_hamming_84(data + 7) << 11),
275                                 serial_mode = C & (1<<11);
276
277                                 /* page on the same magazine? end current page. */
278                         if ((serial_mode || M == m_page_M) && m_page_open)
279                         {
280                                 eDebug("Page End %d %lld", !have_pts, pts);
281                                 handlePageEnd(have_pts, pts);
282                                 m_page_open = 0;
283                         }
284
285                         if ((C & (1<<6)) && (X != 0xFF) && !(C & (1<<5))) /* scan for pages with subtitle bit set */
286                         {
287                                 eDVBServicePMTHandler::subtitleStream s;
288                                 s.pid = m_pid;
289                                 s.subtitling_type = 0x01; // ebu teletext subtitle
290                                 s.teletext_page_number = X & 0xFF;
291                                 s.teletext_magazine_number = M & 7;
292                                 m_found_subtitle_pages.insert(s);
293                         }
294
295                                 /* correct page on correct magazine? open page. */
296                         if (M == m_page_M && X == m_page_X)
297                         {
298                                 eDebug("Page Start %d %lld", !have_pts, pts);
299                                 m_C = C;
300                                 m_Y = Y;
301                                 handlePageStart();
302                                 m_page_open = 1;
303                                 m_box_open = 0;
304                                 handleLine(data + 8, 32);
305                         }
306                 } else if (Y < 26) // directly displayable packet
307                 {
308                         /* data for the selected page ? */
309                         if (M == m_page_M && m_page_open)
310                         {
311                                 m_Y = Y;
312                                 m_box_open = 0;
313                                 handleLine(data, 40);
314                         }
315                 } else if (Y == 26 && m_page_open && M == m_page_M)
316                 {
317 //                      int designation_code = decode_hamming_84(data);
318                         static const unsigned char rev[16] = {
319                                 0x00,0x08,0x04,0x0c,
320                                 0x02,0x0a,0x06,0x0e,
321                                 0x01,0x09,0x05,0x0d,
322                                 0x03,0x0b,0x07,0x0f
323                         };
324                         int display_row=-1, display_column=-1;
325                         for (int a = 1; a < 40; a+=3)
326                         {
327                                 int val;
328                                 data[a] = rev[data[a] >> 4] | (rev[data[a] & 0xf] << 4);
329                                 data[a+1] = rev[data[a+1] >> 4] | (rev[data[a+1] & 0xf] << 4);
330                                 data[a+2] = rev[data[a+2] >> 4] | (rev[data[a+2] & 0xf] << 4);
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)
405                         eDebug("non handled packet M/%d/%d", Y, decode_hamming_84(data));
406                 else if (m_page_open)
407                 {
408                         if (M == m_page_M)
409                                 eDebug("non handled packet X/%d/%d", Y, decode_hamming_84(data));
410                 }
411         }
412 }
413
414 int eDVBTeletextParser::start(int pid)
415 {
416         m_page_open = 0;
417
418         if (m_pes_reader)
419         {
420                 m_pid = pid;
421                 return m_pes_reader->start(pid);
422         }
423         else
424                 return -1;
425 }
426
427 void eDVBTeletextParser::handlePageStart()
428 {
429         if (m_C & (1<<4)) /* erase flag set */
430         {
431                 m_subtitle_page.clear();
432                 m_modifications.clear();
433 //              eDebug("erase page!");
434         }
435 //      else
436 //              eDebug("no erase flag set!");
437 }
438
439 void eDVBTeletextParser::handleLine(unsigned char *data, int len)
440 {
441 /* // hexdump
442         for (int i=0; i<len; ++i)
443                 eDebugNoNewLine("%02x ", decode_odd_parity(data + i));
444         eDebug(""); */
445         if (!m_Y) /* first line is page header, we don't need that. */
446         {
447                 m_double_height = -1;
448                 return;
449         }
450                 
451         if (m_double_height == m_Y)
452         {
453                 m_double_height = -1;
454                 return;
455         }
456
457         int last_was_white = 1, color = 1; /* start with whitespace. start with color=white. (that's unrelated.) */
458
459         static unsigned char out[128];
460
461         int outidx = 0,
462                 Gtriplet = 0,
463                 nat_opts = (m_C & (1<<14) ? 1 : 0) |
464                                         (m_C & (1<<13) ? 2 : 0) |
465                                         (m_C & (1<<12) ? 4 : 0),
466                 nat_subset = NationalOptionSubsetsLookup[Gtriplet*8+nat_opts];
467 /*      eDebug("nat_opts = %d, nat_subset = %d, C121314 = %d%d%d",
468                 nat_opts, nat_subset,
469                 (m_C & (1<<12))?1:0,
470                 (m_C & (1<<13))?1:0,
471                 (m_C & (1<<14))?1:0);*/
472
473 //      eDebug("handle subtitle line: %d len", len);
474         for (int i=0; i<len; ++i)
475         {
476                 std::map<int,unsigned int>::iterator it = m_modifications.find((m_Y<<16)|i);
477                 if (it != m_modifications.end())
478                 {
479                         unsigned int utf8_code = it->second;
480                         if (utf8_code < 0x10)
481                         {
482                                 int mode = utf8_code;
483                                 unsigned char ch = decode_odd_parity(data + i);
484                                 if (ch > 96 && ch < 123)
485                                         utf8_code = diacr_lower_cmap[(ch-97)*15+mode-1];
486                                 else if (ch > 64 && ch < 91)
487                                         utf8_code = diacr_upper_cmap[(ch-65)*15+mode-1];
488                         }
489                         if (utf8_code > 0xFFFFFF)
490                                 out[outidx++]=(utf8_code&0xFF000000)>>24;
491                         if (utf8_code > 0xFFFF)
492                                 out[outidx++]=(utf8_code&0xFF0000)>>16;
493                         if (utf8_code > 0xFF)
494                                 out[outidx++]=(utf8_code&0xFF00)>>8;
495                         if (utf8_code)
496                                 out[outidx++]=utf8_code&0xFF;
497                         m_modifications.erase(it);
498                         continue;
499                 }
500
501                 unsigned char b = decode_odd_parity(data + i);
502
503                 if (b < 0x10) /* spacing attribute */
504                 {
505                         if (b < 8) /* colors */
506                         {
507                                 if (b != color) /* new color is split into a new string */
508                                 {
509                                         addSubtitleString(color, std::string((const char*)out, outidx));
510                                         outidx = 0;
511                                         color = b;
512                                 }
513                         }
514                         else if (b == 0xd)
515                                 m_double_height = m_Y + 1;
516                         else if (b == 0xa)  // close box
517                                 m_box_open=0;
518                         else if (b == 0xb)  // open box
519                                 ++m_box_open;
520                         else
521                                 eDebug("[ignore %x]", b);
522                                 /* ignore other attributes */
523                 } else if (m_box_open>1)
524                 {
525                         //eDebugNoNewLine("%c", b);
526                                 /* no more than one whitespace, only printable chars */
527                         if (((!last_was_white) || (b != ' ')) && (b >= 0x20))
528                         {
529                                 int cur_nat_subset = nat_subset;
530
531                                 if (b == 0x24) // workaround for currency sign.. the only on non latin1 char in G0 set
532                                 {
533                                         cur_nat_subset = 9;
534                                         b = 36;
535                                 }
536
537                                 unsigned char offs = NationalReplaceMap[b];
538                                 if (offs)
539                                 {
540                                         unsigned int utf8_code =
541                                                 NationalOptionSubsets[cur_nat_subset*14+offs];
542                                         if (utf8_code > 0xFFFFFF)
543                                                 out[outidx++]=(utf8_code&0xFF000000)>>24;
544                                         if (utf8_code > 0xFFFF)
545                                                 out[outidx++]=(utf8_code&0xFF0000)>>16;
546                                         if (utf8_code > 0xFF)
547                                                 out[outidx++]=(utf8_code&0xFF00)>>8;
548                                         out[outidx++]=utf8_code&0xFF;
549                                 }
550                                 else
551                                         out[outidx++] = b;
552                                 last_was_white = b == ' ';
553                         }
554                 }
555         }
556         //eDebug("");
557         addSubtitleString(color, std::string((const char*)out, outidx));
558 }
559
560 void eDVBTeletextParser::handlePageEnd(int have_pts, const pts_t &pts)
561 {
562 //      eDebug("handle page end");
563         addSubtitleString(-2, ""); /* end last line */ 
564         
565         m_subtitle_page.m_have_pts = have_pts;
566         m_subtitle_page.m_pts = pts;
567         m_subtitle_page.m_timeout = 90000 * 20; /* 20s */
568         if (m_page_X != 0)
569                 sendSubtitlePage();  /* send assembled subtitle page to display */
570 }
571
572 void eDVBTeletextParser::setPageAndMagazine(int page, int magazine)
573 {
574         if (page > 0)
575                 eDebug("enable teletext subtitle page %x%02x", magazine, page);
576         else
577                 eDebug("disable teletext subtitles");
578         m_page_M = magazine; /* magazine to look for */
579         if (magazine != -1)
580                 m_page_M &= 7;
581         m_page_X = page;  /* page number */
582         if (page != -1)
583                 m_page_X &= 0xFF;
584 }
585
586 void eDVBTeletextParser::connectNewPage(const Slot1<void, const eDVBTeletextSubtitlePage&> &slot, ePtr<eConnection> &connection)
587 {
588         connection = new eConnection(this, m_new_subtitle_page.connect(slot));
589 }
590
591 void eDVBTeletextParser::addSubtitleString(int color, std::string string)
592 {
593 //      eDebug("(%d)add subtitle string: %s, col %d", m_Y, string.c_str(), color);
594         int force_cell = 0;
595
596         if (string.substr(0, 2) == "- ")
597         {
598                 string = string.substr(2);
599                 force_cell = 1;
600         }
601
602 //      eDebug("color %d, m_subtitle_color %d", color, m_subtitle_color);
603         gRGB rgbcol((color & 1) ? 255 : 128, (color & 2) ? 255 : 128, (color & 4) ? 255 : 128);
604         if ((color != m_subtitle_color || force_cell) && !m_subtitle_text.empty() && ((color == -2) || !string.empty()))
605         {
606 //              eDebug("add text |%s|: %d != %d || %d", m_subtitle_text.c_str(), color, m_subtitle_color, force_cell);
607                 m_subtitle_page.m_elements.push_back(eDVBTeletextSubtitlePageElement(rgbcol, m_subtitle_text));
608                 m_subtitle_text = "";
609         } else if (!m_subtitle_text.empty() && m_subtitle_text[m_subtitle_text.size()-1] != ' ')
610                 m_subtitle_text += " ";
611         
612         if (!string.empty())
613         {
614 //              eDebug("set %d as new color", color);
615                 m_subtitle_color = color;
616                 m_subtitle_text += string;
617         }
618 }
619
620 void eDVBTeletextParser::sendSubtitlePage()
621 {
622 //      eDebug("subtitle page:");
623         bool empty=true;
624         if (empty)
625                 for (unsigned int i = 0; i < m_subtitle_page.m_elements.size(); ++i)
626                         if (!m_subtitle_page.m_elements[i].m_text.empty())
627                                 empty=false;
628         if (!empty)
629                 m_new_subtitle_page(m_subtitle_page);
630 }