some teletext subtitles fixes
[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                 /* we are always erasing the page, 
432                    even when the erase flag is not set. */
433                 m_subtitle_page.clear();
434                 m_modifications.clear();
435 //              eDebug("erase page!");
436         }
437 //      else
438 //              eDebug("no erase flag set!");
439 }
440
441 void eDVBTeletextParser::handleLine(unsigned char *data, int len)
442 {
443 /* // hexdump
444         for (int i=0; i<len; ++i)
445                 eDebugNoNewLine("%02x ", decode_odd_parity(data + i));
446         eDebug(""); */
447         if (!m_Y) /* first line is page header, we don't need that. */
448         {
449                 m_double_height = -1;
450                 return;
451         }
452                 
453         if (m_double_height == m_Y)
454         {
455                 m_double_height = -1;
456                 return;
457         }
458
459         int last_was_white = 1, color = 1; /* start with whitespace. start with color=white. (that's unrelated.) */
460
461         static unsigned char out[128];
462
463         int outidx = 0,
464                 Gtriplet = 0,
465                 nat_opts = (m_C & (1<<14) ? 1 : 0) |
466                                         (m_C & (1<<13) ? 2 : 0) |
467                                         (m_C & (1<<12) ? 4 : 0),
468                 nat_subset = NationalOptionSubsetsLookup[Gtriplet*8+nat_opts];
469 /*      eDebug("nat_opts = %d, nat_subset = %d, C121314 = %d%d%d",
470                 nat_opts, nat_subset,
471                 (m_C & (1<<12))?1:0,
472                 (m_C & (1<<13))?1:0,
473                 (m_C & (1<<14))?1:0);*/
474
475 //      eDebug("handle subtitle line: %d len", len);
476         for (int i=0; i<len; ++i)
477         {
478                 std::map<int,unsigned int>::iterator it = m_modifications.find((m_Y<<16)|i);
479                 if (it != m_modifications.end())
480                 {
481                         unsigned int utf8_code = it->second;
482                         if (utf8_code < 0x10)
483                         {
484                                 int mode = utf8_code;
485                                 unsigned char ch = decode_odd_parity(data + i);
486                                 if (ch > 96 && ch < 123)
487                                         utf8_code = diacr_lower_cmap[(ch-97)*15+mode-1];
488                                 else if (ch > 64 && ch < 91)
489                                         utf8_code = diacr_upper_cmap[(ch-65)*15+mode-1];
490                         }
491                         if (utf8_code > 0xFFFFFF)
492                                 out[outidx++]=(utf8_code&0xFF000000)>>24;
493                         if (utf8_code > 0xFFFF)
494                                 out[outidx++]=(utf8_code&0xFF0000)>>16;
495                         if (utf8_code > 0xFF)
496                                 out[outidx++]=(utf8_code&0xFF00)>>8;
497                         if (utf8_code)
498                                 out[outidx++]=utf8_code&0xFF;
499                         m_modifications.erase(it);
500                         continue;
501                 }
502
503                 unsigned char b = decode_odd_parity(data + i);
504
505                 if (b < 0x10) /* spacing attribute */
506                 {
507                         if (b < 8) /* colors */
508                         {
509                                 if (b != color) /* new color is split into a new string */
510                                 {
511                                         addSubtitleString(color, std::string((const char*)out, outidx));
512                                         outidx = 0;
513                                         color = b;
514                                 }
515                         }
516                         else if (b == 0xd)
517                                 m_double_height = m_Y + 1;
518                         else if (b == 0xa)  // close box
519                                 m_box_open=0;
520                         else if (b == 0xb)  // open box
521                                 ++m_box_open;
522                         else
523                                 eDebug("[ignore %x]", b);
524                                 /* ignore other attributes */
525                 } else if (m_box_open>1)
526                 {
527                         //eDebugNoNewLine("%c", b);
528                                 /* no more than one whitespace, only printable chars */
529                         if (((!last_was_white) || (b != ' ')) && (b >= 0x20))
530                         {
531                                 int cur_nat_subset = nat_subset;
532
533                                 if (b == 0x24) // workaround for currency sign.. the only on non latin1 char in G0 set
534                                 {
535                                         cur_nat_subset = 9;
536                                         b = 36;
537                                 }
538
539                                 unsigned char offs = NationalReplaceMap[b];
540                                 if (offs)
541                                 {
542                                         unsigned int utf8_code =
543                                                 NationalOptionSubsets[cur_nat_subset*14+offs];
544                                         if (utf8_code > 0xFFFFFF)
545                                                 out[outidx++]=(utf8_code&0xFF000000)>>24;
546                                         if (utf8_code > 0xFFFF)
547                                                 out[outidx++]=(utf8_code&0xFF0000)>>16;
548                                         if (utf8_code > 0xFF)
549                                                 out[outidx++]=(utf8_code&0xFF00)>>8;
550                                         out[outidx++]=utf8_code&0xFF;
551                                 }
552                                 else
553                                         out[outidx++] = b;
554                                 last_was_white = b == ' ';
555                         }
556                 }
557         }
558         //eDebug("");
559         addSubtitleString(color, std::string((const char*)out, outidx));
560 }
561
562 void eDVBTeletextParser::handlePageEnd(int have_pts, const pts_t &pts)
563 {
564 //      eDebug("handle page end");
565         addSubtitleString(-2, ""); /* end last line */ 
566         
567         m_subtitle_page.m_have_pts = have_pts;
568         m_subtitle_page.m_pts = pts;
569         m_subtitle_page.m_timeout = 90000 * 20; /* 20s */
570         if (m_page_X != 0)
571                 sendSubtitlePage();  /* send assembled subtitle page to display */
572 }
573
574 void eDVBTeletextParser::setPageAndMagazine(int page, int magazine)
575 {
576         if (page > 0)
577                 eDebug("enable teletext subtitle page %x%02x", magazine, page);
578         else
579                 eDebug("disable teletext subtitles");
580         m_page_M = magazine; /* magazine to look for */
581         if (magazine != -1)
582                 m_page_M &= 7;
583         m_page_X = page;  /* page number */
584         if (page != -1)
585                 m_page_X &= 0xFF;
586 }
587
588 void eDVBTeletextParser::connectNewPage(const Slot1<void, const eDVBTeletextSubtitlePage&> &slot, ePtr<eConnection> &connection)
589 {
590         connection = new eConnection(this, m_new_subtitle_page.connect(slot));
591 }
592
593 void eDVBTeletextParser::addSubtitleString(int color, std::string string)
594 {
595 //      eDebug("(%d)add subtitle string: %s, col %d", m_Y, string.c_str(), color);
596         int force_cell = 0;
597
598         if (string.substr(0, 2) == "- ")
599         {
600                 string = string.substr(2);
601                 force_cell = 1;
602         }
603
604 //      eDebug("color %d, m_subtitle_color %d", color, m_subtitle_color);
605         gRGB rgbcol((color & 1) ? 255 : 128, (color & 2) ? 255 : 128, (color & 4) ? 255 : 128);
606         if ((color != m_subtitle_color || force_cell) && !m_subtitle_text.empty() && ((color == -2) || !string.empty()))
607         {
608 //              eDebug("add text |%s|: %d != %d || %d", m_subtitle_text.c_str(), color, m_subtitle_color, force_cell);
609                 m_subtitle_page.m_elements.push_back(eDVBTeletextSubtitlePageElement(rgbcol, m_subtitle_text));
610                 m_subtitle_text = "";
611         } else if (!m_subtitle_text.empty() && m_subtitle_text[m_subtitle_text.size()-1] != ' ')
612                 m_subtitle_text += " ";
613         
614         if (!string.empty())
615         {
616 //              eDebug("set %d as new color", color);
617                 m_subtitle_color = color;
618                 m_subtitle_text += string;
619         }
620 }
621
622 void eDVBTeletextParser::sendSubtitlePage()
623 {
624 //      eDebug("subtitle page:");
625         bool empty=true;
626         if (empty)
627                 for (unsigned int i = 0; i < m_subtitle_page.m_elements.size(); ++i)
628                         if (!m_subtitle_page.m_elements[i].m_text.empty())
629                                 empty=false;
630         if (!empty)
631                 m_new_subtitle_page(m_subtitle_page);
632 }