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