1 #include <lib/base/eerror.h>
2 #include <lib/dvb/teletext.h>
3 #include <lib/dvb/idemux.h>
4 #include <lib/gdi/gpixmap.h>
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)
10 unsigned char NationalOptionSubsetsLookup[16*8] =
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
30 unsigned char NationalReplaceMap[128] =
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
42 // national option subsets (UTF8)
43 // see table 36 in ETSI EN 300 706
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
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
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
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,
128 // This is a very simple en300 706 telext decoder.
129 // It can only decode a single page at a time, thus it's only used
130 // for subtitles. And it ONLY support LATIN Charsets yet!
132 DEFINE_REF(eDVBTeletextParser);
134 /* we asumme error free transmission! */
135 static inline unsigned char decode_odd_parity(unsigned char *b)
138 unsigned char res = 0;
140 if (*b & (0x80 >> i))
145 static inline unsigned char decode_hamming_84(unsigned char *b)
147 return ((*b << 3) & 8) | ((*b ) & 4) | ((*b >> 3) & 2) | ((*b >> 6) & 1);
150 static inline unsigned long decode_hamming_2418(unsigned char *b)
152 static const unsigned char rev[16] = {
158 b[0] = rev[b[0] >> 4] | (rev[b[0] & 0xf] << 4);
159 b[1] = rev[b[1] >> 4] | (rev[b[1] & 0xf] << 4);
160 b[2] = rev[b[2] >> 4] | (rev[b[2] & 0xf] << 4);
161 return ((b[0] & 0x04) >> 2) | ((b[0] & 0x70) >> 3) | ((b[1] & 0x7f) << 4) | ((b[2] & 0x7f) << 11);
164 static int extractPTS(pts_t &pts, unsigned char *pkt)
169 pkt++; // header length
171 if (flags & 0x80) /* PTS present? */
174 pts = ((unsigned long long)(((pkt[0] >> 1) & 7))) << 30;
176 pts |= (pkt[2]>>1) << 15;
185 eDVBTeletextParser::eDVBTeletextParser(iDVBDemux *demux)
187 setStreamID(0xBD); /* as per en 300 472 */
189 setPageAndMagazine(-1, -1);
191 if (demux->createPESReader(eApp, m_pes_reader))
192 eDebug("failed to create teletext subtitle PES reader!");
194 m_pes_reader->connectRead(slot(*this, &eDVBTeletextParser::processData), m_read_connection);
197 eDVBTeletextParser::~eDVBTeletextParser()
201 char *get_bits(int val, int count)
206 for (int i=0; i < count; ++i)
208 buf[(count-i)-1]=val&1?'1':'0';
214 void eDVBTeletextParser::processPESPacket(__u8 *pkt, int len)
216 unsigned char *p = pkt;
219 int have_pts = extractPTS(pts, pkt);
221 p += 4; len -= 4; /* start code, already be verified by pes parser */
222 p += 2; len -= 2; /* length, better use the argument */
224 p += 3; len -= 3; /* pes header */
226 p += 0x24; len -= 0x24; /* skip header */
228 // eDebug("data identifier: %02x", *p);
234 /*unsigned char data_unit_id = */*p++;
235 unsigned char data_unit_length = *p++;
238 if (len < data_unit_length)
240 eDebug("data_unit_length > len");
244 if (data_unit_length != 44)
246 /* eDebug("illegal data unit length %d", data_unit_length); */
250 // if (data_unit_id != 0x03)
252 // /* eDebug("non subtitle data unit id %d", data_unit_id); */
256 /*unsigned char line_offset =*/ *p++; len--;
257 unsigned char framing_code = *p++; len--;
259 int magazine_and_packet_address = decode_hamming_84(p++); len--;
260 magazine_and_packet_address |= decode_hamming_84(p++)<<4; len--;
262 unsigned char *data = p; p += 40; len -= 40;
264 if (framing_code != 0xe4) /* no teletxt data */
267 int M = magazine_and_packet_address & 7,
268 Y = magazine_and_packet_address >> 3;
269 // eDebug("line %d, framing code: %02x, M=%02x, Y=%02x", line_offset, framing_code, m_M, m_Y);
271 if (Y == 0) /* page header */
273 int X = decode_hamming_84(data + 1) * 0x10 + decode_hamming_84(data),
274 // S1 = decode_hamming_84(data + 2),
275 S2C4 = decode_hamming_84(data + 3),
277 // S3 = decode_hamming_84(data + 4),
278 S4C5C6 = decode_hamming_84(data + 5),
280 C = ((S2C4 & 8) ? (1<<4) : 0) |
281 ((S4C5C6 & 0xC) << 3) |
282 (decode_hamming_84(data + 6) << 7) |
283 (decode_hamming_84(data + 7) << 11),
284 serial_mode = C & (1<<11);
286 /* page on the same magazine? end current page. */
287 if ((serial_mode || M == m_page_M) && m_page_open)
289 eDebug("Page End %d %lld", !have_pts, pts);
290 handlePageEnd(!have_pts, pts);
294 if ((C & (1<<6)) && (X != 0xFF) && !(C & (1<<5))) /* scan for pages with subtitle bit set */
296 eDVBServicePMTHandler::subtitleStream s;
298 s.subtitling_type = 0x01; // ebu teletext subtitle
299 s.teletext_page_number = X & 0xFF;
300 s.teletext_magazine_number = M & 7;
301 m_found_subtitle_pages.insert(s);
304 /* correct page on correct magazine? open page. */
305 if (M == m_page_M && X == m_page_X)
307 eDebug("Page Start %d %lld", !have_pts, pts);
313 handleLine(data + 8, 32);
315 } else if (Y < 26) // directly displayable packet
317 /* data for the selected page ? */
318 if (M == m_page_M && m_page_open)
322 handleLine(data, 40);
324 } else if (Y == 26 && m_page_open && M == m_page_M)
326 // int designation_code = decode_hamming_84(data);
327 int display_row=-1, display_column=-1;
328 for (int a = 1; a < 40; a+=3)
331 if ((val=decode_hamming_2418(data+a)) >= 0)
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
342 display_row = addr - 40;
346 eDebugNoNewLine("ignore unimplemented: ");
348 else //0..39 means column 0..39
350 if (display_row != -1)
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.
357 unsigned int ch=data;
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];
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);
378 else if (mode == 15) // char from G2 set
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);
388 eDebugNoNewLine("ignore G2 char < 0x20: ");
391 eDebugNoNewLine("ignore unimplemented: ");
394 eDebugNoNewLine("row is not selected.. ignore: ");
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));
403 /*eDebug("non handled packet 30, 31", Y, decode_hamming_84(data))*/;
404 else if (Y == 29 && M == m_page_M)
406 int designation_code = decode_hamming_84(data++);
407 if (designation_code == 0) // 29/0
409 m_M29_t1 = decode_hamming_2418(data);
410 m_M29_t2 = decode_hamming_2418(data+3);
411 if ((m_M29_t1 & 0xF) == 0) // format1
414 eDebug("non handled packet M/%d/0 format %d", Y, m_M29_t1 & 0xF);
417 eDebug("non handled packet M/%d/%d", Y, designation_code);
419 else if (m_page_open && M == m_page_M)
421 int designation_code = decode_hamming_84(data++);
422 if (Y == 28 && designation_code == 0) // 28/0
425 m_X28_t1 = decode_hamming_2418(data);
426 m_X28_t2 = decode_hamming_2418(data+3);
427 if ((m_X28_t1 & 0xF) == 0) // format1
430 eDebug("non handled packet X/%d/0 format %d", Y, m_X28_t1 & 0xF);
435 int tripletX = decode_hamming_2418(data+i);
440 if ((m_X28_t1 & 0xF) == 0) // format1
447 char *c = get_bits(tripletX, 18);
450 eDebugNoNewLine("%c", c[x]);
454 eDebug("decode_hamming_2418 failed!\n");
460 eDebug("non handled packet X/%d/%d", Y, designation_code);
465 int eDVBTeletextParser::start(int pid)
472 return m_pes_reader->start(pid);
478 void eDVBTeletextParser::handlePageStart()
480 if (m_C & (1<<4)) /* erase flag set */
482 m_subtitle_page.clear();
483 m_modifications.clear();
485 // eDebug("erase page!");
488 // eDebug("no erase flag set!");
489 m_current_source_line = -1;
492 void eDVBTeletextParser::handleLine(unsigned char *data, int len)
495 for (int i=0; i<len; ++i)
496 eDebugNoNewLine("%02x ", decode_odd_parity(data + i));
499 m_subtitle_page.clearLine(m_Y);
501 if (!m_Y) /* first line is page header, we don't need that. */
503 m_double_height = -1;
507 if (m_double_height == m_Y)
509 m_double_height = -1;
513 int last_was_white = 1, color = 1; /* start with whitespace. start with color=white. (that's unrelated.) */
515 static unsigned char out[128];
519 nat_opts = (m_C & (1<<14) ? 1 : 0) |
520 (m_C & (1<<13) ? 2 : 0) |
521 (m_C & (1<<12) ? 4 : 0),
522 nat_subset_2 = NationalOptionSubsetsLookup[Gtriplet*8+nat_opts],
523 nat_subset = nat_subset_2,
528 nat_subset = NationalOptionSubsetsLookup[(m_X28_t1 >> 7) & 0x7F];
529 nat_subset_2 = NationalOptionSubsetsLookup[((m_X28_t1 >> 14) & 0xF) | ((m_X28_t2 & 7) << 4)];
530 // eDebug("X/28/0 nat_subset %d, nat_subset2 %d", nat_subset, nat_subset_2);
532 else if (m_M29_0_valid)
534 nat_subset = NationalOptionSubsetsLookup[(m_M29_t1 >> 7) & 0x7F];
535 nat_subset_2 = NationalOptionSubsetsLookup[((m_M29_t1 >> 14) & 0xF) | ((m_M29_t2 & 7) << 4)];
536 // eDebug("M/29/0 nat_subset %d, nat_subset2 %d", nat_subset, nat_subset_2);
539 eDebug("nat_opts = %d, nat_subset = %d, C121314 = %d%d%d",
540 nat_opts, nat_subset,
543 (m_C & (1<<14))?1:0);*/
545 // eDebug("handle subtitle line: %d len", len);
546 for (int i=0; i<len; ++i)
548 unsigned char b = decode_odd_parity(data + i);
549 std::map<int,unsigned int>::iterator it = m_modifications.find((m_Y<<16)|i);
551 if (it != m_modifications.end())
553 unsigned int utf8_code = it->second;
554 // eDebugNoNewLine("%c[%d]", b, b);
555 if (utf8_code < 0x10)
557 int mode = utf8_code;
558 if (b > 96 && b < 123)
559 utf8_code = diacr_lower_cmap[(b-97)*15+mode-1];
560 else if (b > 64 && b < 91)
561 utf8_code = diacr_upper_cmap[(b-65)*15+mode-1];
563 if (utf8_code > 0xFFFFFF)
564 out[outidx++]=(utf8_code&0xFF000000)>>24;
565 if (utf8_code > 0xFFFF)
566 out[outidx++]=(utf8_code&0xFF0000)>>16;
567 if (utf8_code > 0xFF)
568 out[outidx++]=(utf8_code&0xFF00)>>8;
570 out[outidx++]=utf8_code&0xFF;
571 m_modifications.erase(it);
575 if (b < 0x10) /* spacing attribute */
577 if (b < 8) /* colors */
579 if (b != color) /* new color is split into a new string */
581 addSubtitleString(color, std::string((const char*)out, outidx), m_Y);
587 m_double_height = m_Y + 1;
588 else if (b == 0xa) // close box
590 else if (b == 0xb) // open box
593 eDebug("[ignore %x]", b);
594 /* ignore other attributes */
595 } else if (m_box_open>1)
597 // eDebugNoNewLine("%c(%d)", b, b);
598 /* no more than one whitespace, only printable chars */
599 if (((!last_was_white) || (b != ' ')) && (b >= 0x20))
601 int cur_nat_subset = second_G0_set ? nat_subset_2 : nat_subset;
603 unsigned char offs = NationalReplaceMap[b];
606 unsigned int utf8_code =
607 NationalOptionSubsets[cur_nat_subset*14+offs];
608 if (utf8_code > 0xFFFFFF)
609 out[outidx++]=(utf8_code&0xFF000000)>>24;
610 if (utf8_code > 0xFFFF)
611 out[outidx++]=(utf8_code&0xFF0000)>>16;
612 if (utf8_code > 0xFF)
613 out[outidx++]=(utf8_code&0xFF00)>>8;
614 out[outidx++]=utf8_code&0xFF;
618 last_was_white = b == ' ';
620 else if (b == 0x1b) // ESC ... switch between default G0 and second G0 charset
625 addSubtitleString(color, std::string((const char*)out, outidx), m_Y);
628 void eDVBTeletextParser::handlePageEnd(int have_pts, const pts_t &pts)
630 // eDebug("handle page end");
631 addSubtitleString(-2, "", -1); /* end last line */
633 m_subtitle_page.m_have_pts = have_pts;
634 m_subtitle_page.m_pts = pts;
635 m_subtitle_page.m_timeout = 90000 * 20; /* 20s */
637 sendSubtitlePage(); /* send assembled subtitle page to display */
640 void eDVBTeletextParser::setPageAndMagazine(int page, int magazine)
643 eDebug("enable teletext subtitle page %x%02x", magazine, page);
645 eDebug("disable teletext subtitles");
647 m_page_M = magazine; /* magazine to look for */
650 m_page_X = page; /* page number */
655 void eDVBTeletextParser::connectNewPage(const Slot1<void, const eDVBTeletextSubtitlePage&> &slot, ePtr<eConnection> &connection)
657 connection = new eConnection(this, m_new_subtitle_page.connect(slot));
660 void eDVBTeletextParser::addSubtitleString(int color, std::string string, int source_line)
662 // eDebug("(%d)add subtitle string: %s, col %d", m_Y, string.c_str(), color);
665 if (string.substr(0, 2) == "- ")
667 string = string.substr(2);
671 // eDebug("color %d, m_subtitle_color %d", color, m_subtitle_color);
672 if ((color != m_subtitle_color || force_cell) && !m_subtitle_text.empty() && ((color == -2) || !string.empty()))
674 const gRGB pal[4] = { gRGB(64, 0, 76), gRGB(180, 40, 86), gRGB(160, 170, 105), gRGB(250, 200, 140) };
675 // eDebug("add text |%s|: %d != %d || %d", m_subtitle_text.c_str(), color, m_subtitle_color, force_cell);
676 m_subtitle_page.m_elements.push_back(eDVBTeletextSubtitlePageElement(pal[m_subtitle_color & 3], m_subtitle_text, m_current_source_line));
677 m_current_source_line = -1;
678 m_subtitle_text = "";
679 } else if (!m_subtitle_text.empty() && m_subtitle_text[m_subtitle_text.size()-1] != ' ')
680 m_subtitle_text += " ";
682 if (m_current_source_line == -1)
683 m_current_source_line = source_line;
687 // eDebug("set %d as new color", color);
689 m_subtitle_color = color;
690 m_subtitle_text += string;
694 void eDVBTeletextParser::sendSubtitlePage()
696 // eDebug("subtitle page:");
697 bool send=m_C & (1<<4);
698 for (unsigned int i = 0; i < m_subtitle_page.m_elements.size(); ++i)
699 if (!m_subtitle_page.m_elements[i].m_text.empty())
702 m_new_subtitle_page(m_subtitle_page);