clear non processed subtitles on skip forward / backward
[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                                 eDebug("Page End %d %lld", !have_pts, pts);
259                                 handlePageEnd(have_pts, pts);
260                                 m_page_open = 0;
261                         }
262
263                         if ((C & (1<<6)) && (X != 0xFF) && !(C & (1<<5))) /* scan for pages with subtitle bit set */
264                         {
265                                 eDVBServicePMTHandler::subtitleStream s;
266                                 s.pid = m_pid;
267                                 s.subtitling_type = 0x01; // ebu teletext subtitle
268                                 s.teletext_page_number = X & 0xFF;
269                                 s.teletext_magazine_number = M & 7;
270                                 m_found_subtitle_pages.insert(s);
271                         }
272
273                                 /* correct page on correct magazine? open page. */
274                         if (M == m_page_M && X == m_page_X)
275                         {
276                                 eDebug("Page Start %d %lld", !have_pts, pts);
277                                 handlePageStart();
278                                 m_subtitle_page.m_C = C;
279                                 m_subtitle_page.m_Y = Y; 
280                                 m_page_open = 1;
281                                 m_box_open = 0;
282                                 handleLine(data + 8, 32);
283                         }
284                 } else if (Y < 26) // directly displayable packet
285                 {
286                         /* data for the selected page ? */
287                         if (M == m_page_M && m_page_open)
288                         {
289                                 m_subtitle_page.m_Y = Y;
290                                 m_box_open = 0;
291                                 handleLine(data, 40);
292                         }
293                 } else if (Y == 26 && m_page_open && M == m_page_M)
294                 {
295 //                      int designation_code = decode_hamming_84(data);
296                         static const unsigned char rev[16] = {
297                                 0x00,0x08,0x04,0x0c,
298                                 0x02,0x0a,0x06,0x0e,
299                                 0x01,0x09,0x05,0x0d,
300                                 0x03,0x0b,0x07,0x0f
301                         };
302                         int display_row=-1, display_column=-1;
303                         for (int a = 1; a < 40; a+=3)
304                         {
305                                 int val;
306                                 data[a] = rev[data[a] >> 4] | (rev[data[a] & 0xf] << 4);
307                                 data[a+1] = rev[data[a+1] >> 4] | (rev[data[a+1] & 0xf] << 4);
308                                 data[a+2] = rev[data[a+2] >> 4] | (rev[data[a+2] & 0xf] << 4);
309                                 if ((val=decode_hamming_2418(data+a)) >= 0)
310                                 {
311                                         unsigned char addr = val & 0x3F;
312                                         unsigned char mode = (val >> 6) & 0x1F;
313                                         unsigned char data = (val >> 11) & 0x7F;
314                                         if (addr == 0x3f && mode == 0x1f) // termination marker
315                                                 break;
316                                         if (addr >= 40)
317                                         {
318                                                 if (mode == 4)
319                                                 {
320                                                         display_row = addr - 40;
321                                                         continue;
322                                                 }
323                                                 else
324                                                         eDebugNoNewLine("ignore unimplemented: ");
325                                         }
326                                         else //0..39 means column 0..39
327                                         {
328                                                 if (display_row != -1)
329                                                 {
330                                                         display_column = addr;
331 //                                                      eDebugNoNewLine("PosX(%d) ", display_column);
332 //                                                      eDebugNoNewLine("PosY(%d) ", display_row);
333                                                         if (mode > 15) //char from G0 set w/ diacr.
334                                                         {
335                                                                 unsigned int ch=data;
336                                                                 if (!mode&0xF)
337                                                                 {
338                                                                         if (data == 0x2A)
339                                                                                 ch = '@';
340                                                                 }
341                                                                 else
342                                                                 {
343                                                                         if (ch > 96 && ch < 123)
344                                                                                 ch = diacr_lower_cmap[(ch-97)*15+(mode&0xF)-1];
345                                                                         else if (ch > 64 && ch < 91)
346                                                                                 ch = diacr_upper_cmap[(ch-65)*15+(mode&0xF)-1];
347                                                                 }
348                                                                 m_modifications[(display_row<<16)|display_column] = ch ? ch : data;
349 //                                                              eDebug("char(%04x) w/ diacr. mark", ch);
350                                                                 continue;
351                                                         }
352                                                         else if (mode == 15) // char from G2 set
353                                                         {
354                                                                 if (data > 0x19)
355                                                                 {
356                                                                         unsigned int ch=Latin_G2_set[data-0x20];
357                                                                         m_modifications[(display_row<<16)|display_column] = ch;
358 //                                                                      eDebug("char(%04x) from G2 set", ch);
359                                                                         continue;
360                                                                 }
361                                                                 else
362                                                                         eDebugNoNewLine("ignore G2 char < 0x20: ");
363                                                         }
364                                                         else
365                                                                 eDebugNoNewLine("ignore unimplemented: ");
366                                                 }
367                                                 else
368                                                         eDebugNoNewLine("row is not selected.. ignore: ");
369                                         }
370                                         eDebugNoNewLine("triplet = %08x(%s) ", val, get_bits(val, 18));
371                                         eDebugNoNewLine("address = %02x(%s) ", addr, get_bits(addr, 6));
372                                         eDebugNoNewLine("mode = %02x(%s) ", mode, get_bits(mode, 5));
373                                         eDebug("data = %02x(%s)", data, get_bits(data, 7));
374                                 }
375                         }
376                 } else if (Y > 29)
377                         /*eDebug("non handled packet 30, 31", Y, decode_hamming_84(data))*/;
378                 else if (Y == 29)
379                         eDebug("non handled packet M/%d/%d", Y, decode_hamming_84(data));
380                 else if (m_page_open)
381                 {
382                         if (M == m_page_M)
383                                 eDebug("non handled packet X/%d/%d", Y, decode_hamming_84(data));
384                 }
385         }
386 }
387
388 int eDVBTeletextParser::start(int pid)
389 {
390         m_page_open = 0;
391
392         if (m_pes_reader)
393         {
394                 m_pid = pid;
395                 return m_pes_reader->start(pid);
396         }
397         else
398                 return -1;
399 }
400
401 void eDVBTeletextParser::handlePageStart()
402 {
403 //      if (m_C & (1<<4)) /* erase flag set */
404
405                 /* we are always erasing the page, 
406                    even when the erase flag is not set. */
407         m_subtitle_page.clear();
408         m_modifications.clear();
409 }
410
411 void eDVBTeletextParser::handleLine(unsigned char *data, int len)
412 {
413 /* // hexdump
414         for (int i=0; i<len; ++i)
415                 eDebugNoNewLine("%02x ", decode_odd_parity(data + i));
416         eDebug(""); */
417         if (!m_subtitle_page.m_Y) /* first line is page header, we don't need that. */
418         {
419                 m_double_height = -1;
420                 return;
421         }
422                 
423         if (m_double_height == m_subtitle_page.m_Y)
424         {
425                 m_double_height = -1;
426                 return;
427         }
428
429         int last_was_white = 1, color = 1; /* start with whitespace. start with color=white. (that's unrelated.) */
430
431         static unsigned char out[128];
432
433         int outidx = 0,
434                 Gtriplet = 0,
435                 nat_opts = (m_subtitle_page.m_C & (1<<14) ? 1 : 0) |
436                                         (m_subtitle_page.m_C & (1<<13) ? 2 : 0) |
437                                         (m_subtitle_page.m_C & (1<<12) ? 4 : 0),
438                 nat_subset = NationalOptionSubsetsLookup[Gtriplet*8+nat_opts];
439 /*      eDebug("nat_opts = %d, nat_subset = %d, C121314 = %d%d%d",
440                 nat_opts, nat_subset,
441                 (m_subtitle_page.m_C & (1<<12))?1:0,
442                 (m_subtitle_page.m_C & (1<<13))?1:0,
443                 (m_subtitle_page.m_C & (1<<14))?1:0);*/
444
445 //      eDebug("handle subtitle line: %d len", len);
446         for (int i=0; i<len; ++i)
447         {
448                 std::map<int,unsigned int>::iterator it = m_modifications.find((m_subtitle_page.m_Y<<16)|i);
449                 if (it != m_modifications.end())
450                 {
451                         unsigned int utf8_code = it->second;
452                         if (utf8_code > 0xFFFFFF)
453                                 out[outidx++]=(utf8_code&0xFF000000)>>24;
454                         if (utf8_code > 0xFFFF)
455                                 out[outidx++]=(utf8_code&0xFF0000)>>16;
456                         if (utf8_code > 0xFF)
457                                 out[outidx++]=(utf8_code&0xFF00)>>8;
458                         out[outidx++]=utf8_code&0xFF;
459                         continue;
460                 }
461
462                 unsigned char b = decode_odd_parity(data + i);
463
464                 if (b < 0x10) /* spacing attribute */
465                 {
466                         if (b < 8) /* colors */
467                         {
468                                 if (b != color) /* new color is split into a new string */
469                                 {
470                                         addSubtitleString(color, std::string((const char*)out, outidx));
471                                         outidx = 0;
472                                         color = b;
473                                 }
474                         }
475                         else if (b == 0xd)
476                                 m_double_height = m_subtitle_page.m_Y + 1;
477                         else if (b == 0xa)  // close box
478                                 m_box_open=0;
479                         else if (b == 0xb)  // open box
480                                 ++m_box_open;
481                         else
482                                 eDebug("[ignore %x]", b);
483                                 /* ignore other attributes */
484                 } else if (m_box_open>1)
485                 {
486                         //eDebugNoNewLine("%c", b);
487                                 /* no more than one whitespace, only printable chars */
488                         if (((!last_was_white) || (b != ' ')) && (b >= 0x20))
489                         {
490                                 int cur_nat_subset = nat_subset;
491
492                                 if (b == 0x24) // workaround for currency sign.. the only on non latin1 char in G0 set
493                                 {
494                                         cur_nat_subset = 9;
495                                         b = 36;
496                                 }
497
498                                 unsigned char offs = NationalReplaceMap[b];
499                                 if (offs)
500                                 {
501                                         unsigned int utf8_code =
502                                                 NationalOptionSubsets[cur_nat_subset*14+offs];
503                                         if (utf8_code > 0xFFFFFF)
504                                                 out[outidx++]=(utf8_code&0xFF000000)>>24;
505                                         if (utf8_code > 0xFFFF)
506                                                 out[outidx++]=(utf8_code&0xFF0000)>>16;
507                                         if (utf8_code > 0xFF)
508                                                 out[outidx++]=(utf8_code&0xFF00)>>8;
509                                         out[outidx++]=utf8_code&0xFF;
510                                 }
511                                 else
512                                         out[outidx++] = b;
513                                 last_was_white = b == ' ';
514                         }
515                 }
516         }
517         //eDebug("");
518         addSubtitleString(color, std::string((const char*)out, outidx));
519 }
520
521 void eDVBTeletextParser::handlePageEnd(int have_pts, const pts_t &pts)
522 {
523 //      eDebug("handle page end");
524         addSubtitleString(-2, ""); /* end last line */ 
525         
526         m_subtitle_page.m_have_pts = have_pts;
527         m_subtitle_page.m_pts = pts;
528         m_subtitle_page.m_timeout = 90000 * 20; /* 20s */
529         if (m_page_X != 0)
530                 sendSubtitlePage();  /* send assembled subtitle page to display */
531 }
532
533 void eDVBTeletextParser::setPageAndMagazine(int page, int magazine)
534 {
535         if (page > 0)
536                 eDebug("enable teletext subtitle page %x%02x", magazine, page);
537         else
538                 eDebug("disable teletext subtitles");
539         m_page_M = magazine; /* magazine to look for */
540         if (magazine != -1)
541                 m_page_M &= 7;
542         m_page_X = page;  /* page number */
543         if (page != -1)
544                 m_page_X &= 0xFF;
545 }
546
547 void eDVBTeletextParser::connectNewPage(const Slot1<void, const eDVBTeletextSubtitlePage&> &slot, ePtr<eConnection> &connection)
548 {
549         connection = new eConnection(this, m_new_subtitle_page.connect(slot));
550 }
551
552 void eDVBTeletextParser::addSubtitleString(int color, std::string string)
553 {
554 //      eDebug("(%d)add subtitle string: %s, col %d", m_subtitle_page.m_Y, string.c_str(), color);
555         int force_cell = 0;
556
557         if (string.substr(0, 2) == "- ")
558         {
559                 string = string.substr(2);
560                 force_cell = 1;
561         }
562
563 //      eDebug("color %d, m_subtitle_color %d", color, m_subtitle_color);
564         gRGB rgbcol((color & 1) ? 255 : 128, (color & 2) ? 255 : 128, (color & 4) ? 255 : 128);
565         if ((color != m_subtitle_color || force_cell) && !m_subtitle_text.empty() && ((color == -2) || !string.empty()))
566         {
567 //              eDebug("add text |%s|: %d != %d || %d", m_subtitle_text.c_str(), color, m_subtitle_color, force_cell);
568                 m_subtitle_page.m_elements.push_back(eDVBTeletextSubtitlePageElement(rgbcol, m_subtitle_text));
569                 m_subtitle_text = "";
570         } else if (!m_subtitle_text.empty() && m_subtitle_text[m_subtitle_text.size()-1] != ' ')
571                 m_subtitle_text += " ";
572         
573         if (!string.empty())
574         {
575 //              eDebug("set %d as new color", color);
576                 m_subtitle_color = color;
577                 m_subtitle_text += string;
578         }
579 }
580
581 void eDVBTeletextParser::sendSubtitlePage()
582 {
583 //      eDebug("subtitle page:");
584         bool empty=true;
585         if (empty)
586                 for (unsigned int i = 0; i < m_subtitle_page.m_elements.size(); ++i)
587                         if (!m_subtitle_page.m_elements[i].m_text.empty())
588                                 empty=false;
589         if (!empty)
590                 m_new_subtitle_page(m_subtitle_page);
591 }