]> git.vomp.tv Git - vompclient.git/blob - dvbsubtitles.cc
Fixed Scaling in Jpeg handling, add Subtitles handling for HD resolutions(untested...
[vompclient.git] / dvbsubtitles.cc
1 /*
2     Copyright 2008 Mark Calderbank
3
4     This file is part of VOMP.
5
6     VOMP is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     VOMP is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with VOMP; if not, write to the Free Software Foundation, Inc.,
18     51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19 */
20 /*  Sections of code and general methods adopted from VDR version 1.6.0
21  *  dvbsubtitle.c (GPL version 2 or later)
22  *  Original author: Marco Schluessler <marco@lordzodiac.de>
23  *  With some input from the "subtitle plugin"
24  *  by Pekka Virtanen <pekka.virtanen@sci.fi>
25 */
26
27 /*  Reference ETSI EN 300 743 V1.3.1 (2006-11) */
28
29 #include "dvbsubtitles.h"
30 #include "demuxer.h"
31 #include "osdreceiver.h"
32 #include "video.h"
33 #include "log.h"
34
35 DVBSubtitleCLUT::DVBSubtitleCLUT()
36 : version(0xFF),
37   palette2(2),
38   palette4(4),
39   palette8(8)
40 {
41   for (int i = 0; i < 4; ++i) palette2.setColour(i, defaultPalette2[i]);
42   for (int i = 0; i < 16; ++i) palette4.setColour(i, defaultPalette4[i]);
43   for (int i = 0; i < 256; ++i) palette8.setColour(i, defaultPalette8[i]);
44 }
45
46 const ULONG DVBSubtitleCLUT::defaultPalette2[4] = {
47   0x00000000, 0xFFFFFFFF, 0xFF000000, 0xFF808080
48 };
49
50 const ULONG DVBSubtitleCLUT::defaultPalette4[16] = {
51   0x00000000, 0xFFFF0000, 0xFF00FF00, 0xFFFFFF00,
52   0xFF0000FF, 0xFFFF00FF, 0xFF00FFFF, 0xFFFFFFFF,
53   0xFF000000, 0xFF800000, 0xFF008000, 0xFF808000,
54   0xFF000080, 0xFF800080, 0xFF008080, 0xFF808080
55 };
56
57 const ULONG DVBSubtitleCLUT::defaultPalette8[256] = {
58   0x00000000, 0x40FF0000, 0x4000FF00, 0x40FFFF00, // 0000 0000
59   0x400000FF, 0x40FF00FF, 0x4000FFFF, 0x40FFFFFF, // 0000 0100
60   0x80000000, 0x80550000, 0x80005500, 0x80555500, // 0000 1000
61   0x80000055, 0x80550055, 0x80005555, 0x80555555, // 0000 1100
62   0xFFAA0000, 0xFFFF0000, 0xFFAA5500, 0xFFFF5500, // 0001 0000
63   0xFFAA0055, 0xFFFF0055, 0xFFAA5555, 0xFFFF5555, // 0001 0100
64   0x80AA0000, 0x80FF0000, 0x80AA5500, 0x80FF5500, // 0001 1000
65   0x80AA0055, 0x80FF0055, 0x80AA5555, 0x80FF5555, // 0001 1100
66   0xFF00AA00, 0xFF55AA00, 0xFF00FF00, 0xFF55FF00, // 0010 0000
67   0xFF00AA55, 0xFF55AA55, 0xFF00FF55, 0xFF55FF55, // 0010 0100
68   0x8000AA00, 0x8055AA00, 0x8000FF00, 0x8055FF00, // 0010 1000
69   0x8000AA55, 0x8055AA55, 0x8000FF55, 0x8055FF55, // 0010 1100
70   0xFFAAAA00, 0xFFFFAA00, 0xFFAAFF00, 0xFFFFFF00, // 0011 0000
71   0xFFAAAA55, 0xFFFFAA55, 0xFFAAFF55, 0xFFFFFF55, // 0011 0100
72   0x80AAAA00, 0x80FFAA00, 0x80AAFF00, 0x80FFFF00, // 0011 1000
73   0x80AAAA55, 0x80FFAA55, 0x80AAFF55, 0x80FFFF55, // 0011 1100
74   0xFF0000AA, 0xFF5500AA, 0xFF0055AA, 0xFF5555AA, // 0100 0000
75   0xFF0000FF, 0xFF5500FF, 0xFF0055FF, 0xFF5555FF, // 0100 0100
76   0x800000AA, 0x805500AA, 0x800055AA, 0x805555AA, // 0100 1000
77   0x800000FF, 0x805500FF, 0x800055FF, 0x805555FF, // 0100 1100
78   0xFFAA00AA, 0xFFFF00AA, 0xFFAA55AA, 0xFFFF55AA, // 0101 0000
79   0xFFAA00FF, 0xFFFF00FF, 0xFFAA55FF, 0xFFFF55FF, // 0101 0100
80   0x80AA00AA, 0x80FF00AA, 0x80AA55AA, 0x80FF55AA, // 0101 1000
81   0x80AA00FF, 0x80FF00FF, 0x80AA55FF, 0x80FF55FF, // 0101 1100
82   0xFF00AAAA, 0xFF55AAAA, 0xFF00FFAA, 0xFF55FFAA, // 0110 0000
83   0xFF00AAFF, 0xFF55AAFF, 0xFF00FFFF, 0xFF55FFFF, // 0110 0100
84   0x8000AAAA, 0x8055AAAA, 0x8000FFAA, 0x8055FFAA, // 0110 1000
85   0x8000AAFF, 0x8055AAFF, 0x8000FFFF, 0x8055FFFF, // 0110 1100
86   0xFFAAAAAA, 0xFFFFAAAA, 0xFFAAFFAA, 0xFFFFFFAA, // 0111 0000
87   0xFFAAAAFF, 0xFFFFAAFF, 0xFFAAFFFF, 0xFFFFFFFF, // 0111 0100
88   0x80AAAAAA, 0x80FFAAAA, 0x80AAFFAA, 0x80FFFFAA, // 0111 1000
89   0x80AAAAFF, 0x80FFAAFF, 0x80AAFFFF, 0x80FFFFFF, // 0111 1100
90   0xFF808080, 0xFFAA8080, 0xFF80AA80, 0xFFAAAA80, // 1000 0000
91   0xFF8080AA, 0xFFAA80AA, 0xFF80AAAA, 0xFFAAAAAA, // 1000 0100
92   0xFF000000, 0xFF2A0000, 0xFF002A00, 0xFF2A2A00, // 1000 1000
93   0xFF00002A, 0xFF2A002A, 0xFF002A2A, 0xFF2A2A2A, // 1000 1100
94   0xFFD58080, 0xFFFF8080, 0xFFD5AA80, 0xFFFFAA80, // 1001 0000
95   0xFFD580AA, 0xFFFF80AA, 0xFFD5AAAA, 0xFFFFAAAA, // 1001 0100
96   0xFF550000, 0xFF7F0000, 0xFF552A00, 0xFF7F2A00, // 1001 1000
97   0xFF55002A, 0xFF7F002A, 0xFF552A2A, 0xFF7F2A2A, // 1001 1100
98   0xFF80D580, 0xFFAAD580, 0xFF80FF80, 0xFFAAFF80, // 1010 0000
99   0xFF80D5AA, 0xFFAAD5AA, 0xFF80FFAA, 0xFFAAFFAA, // 1010 0100
100   0xFF005500, 0xFF2A5500, 0xFF007F00, 0xFF2A7F00, // 1010 1000
101   0xFF00552A, 0xFF2A552A, 0xFF007F2A, 0xFF2A7F2A, // 1010 1100
102   0xFFD5D580, 0xFFFFD580, 0xFFD5FF80, 0xFFFFFF80, // 1011 0000
103   0xFFD5D5AA, 0xFFFFD5AA, 0xFFD5FFAA, 0xFFFFFFAA, // 1011 0100
104   0xFF555500, 0xFF7F5500, 0xFF557F00, 0xFF7F7F00, // 1011 1000
105   0xFF55552A, 0xFF7F552A, 0xFF557F2A, 0xFF7F7F2A, // 1011 1100
106   0xFF8080D5, 0xFFAA80D5, 0xFF80AAD5, 0xFFAAAAD5, // 1100 0000
107   0xFF8080FF, 0xFFAA80FF, 0xFF80AAFF, 0xFFAAAAFF, // 1100 0100
108   0xFF000055, 0xFF2A0055, 0xFF002A55, 0xFF2A2A55, // 1100 1000
109   0xFF00007F, 0xFF2A007F, 0xFF002A7F, 0xFF2A2A7F, // 1100 1100
110   0xFFD580D5, 0xFFFF80D5, 0xFFD5AAD5, 0xFFFFAAD5, // 1101 0000
111   0xFFD580FF, 0xFFFF80FF, 0xFFD5AAFF, 0xFFFFAAFF, // 1101 0100
112   0xFF550055, 0xFF7F0055, 0xFF552A55, 0xFF7F2A55, // 1101 1000
113   0xFF55007F, 0xFF7F007F, 0xFF552A7F, 0xFF7F2A7F, // 1101 1100
114   0xFF80D5D5, 0xFFAAD5D5, 0xFF80FFD5, 0xFFAAFFD5, // 1110 0000
115   0xFF80D5FF, 0xFFAAD5FF, 0xFF80FFFF, 0xFFAAFFFF, // 1110 0100
116   0xFF005555, 0xFF2A5555, 0xFF007F55, 0xFF2A7F55, // 1110 1000
117   0xFF00557F, 0xFF2A557F, 0xFF007F7F, 0xFF2A7F7F, // 1110 1100
118   0xFFD5D5D5, 0xFFFFD5D5, 0xFFD5FFD5, 0xFFFFFFD5, // 1111 0000
119   0xFFD5D5FF, 0xFFFFD5FF, 0xFFD5FFFF, 0xFFFFFFFF, // 1111 0100
120   0xFF555555, 0xFF7F5555, 0xFF557F55, 0xFF7F7F55, // 1111 1000
121   0xFF55557F, 0xFF7F557F, 0xFF557F7F, 0xFF7F7F7F, // 1111 1100
122 };
123
124 void DVBSubtitleCLUT::setEntry(int bpp, UINT index, UCHAR Y, UCHAR Cr, UCHAR Cb, UCHAR T)
125 {
126   UCHAR Y2 = Y;
127   UCHAR A;
128   if (Y2 == 0) { Y2 = 16; A = 0; } else A = 255 - T;
129   switch (bpp)
130   {
131     case 2: palette2.setYCrCbA(index, Y2, Cr, Cb, A); break;
132     case 4: palette4.setYCrCbA(index, Y2, Cr, Cb, A); break;
133     case 8: palette8.setYCrCbA(index, Y2, Cr, Cb, A); break;
134   }
135 }
136
137 const Palette& DVBSubtitleCLUT::getPalette(int bpp) const
138 {
139   switch (bpp)
140   {
141     case 2: return palette2; break;
142     case 4: return palette4; break;
143     default: return palette8; break;
144   }
145 }
146
147 DVBSubtitleRegion::DVBSubtitleRegion()
148 : version(0xFF),
149   level(0),
150   CLUTid(0)
151 {}
152
153 void DVBSubtitleRegion::setDepth(UINT d)
154 {
155   if (d < 4) palette.setBpp(1 << d);
156 }
157
158 DVBSubtitlePage::DVBSubtitlePage()
159 : pts(0),
160   dirty(false),
161   version(0xFF),
162   timeout(0),
163   state(0),
164   serviceAcquired(false)
165 {}
166
167 void DVBSubtitlePage::setState(UCHAR s)
168 {
169   state = s;
170   if (state == 1)
171     serviceAcquired = true;
172   if (state == 2) // Mode change
173   {
174     serviceAcquired = true;
175     dirty = true;
176     regions.clear();
177     currentDisplay.clear();
178     pendingDisplay.clear();
179     CLUTs.clear();
180   }
181 }
182
183 void DVBSubtitlePage::updateRegionPalettes(const DVBSubtitleCLUT& clut)
184 {
185   RegionMap::iterator region;
186   for (region = regions.begin(); region != regions.end(); ++region)
187     if (region->second.CLUTid == clut.CLUTid)
188       region->second.palette = clut.getPalette(region->second.palette.getBpp());
189 }
190
191 DVBSubtitleDisplayDefinition::DVBSubtitleDisplayDefinition()
192 {
193         version=0xFF;
194         displaywindow=false;
195 }
196
197 class DVBSubtitleObject
198 {
199   private:
200     UINT objectID;
201     UCHAR nonModColourFlag;
202     struct RegionRef
203     {
204       DVBSubtitleRegion* region;
205       UINT horizontalPosition;
206       UINT verticalPosition;
207     };
208     typedef std::vector<struct RegionRef> UseList;
209     UseList useList;
210     UCHAR map2to4[4];
211     UCHAR map2to8[4];
212     UCHAR map4to8[16];
213     void initMapTables();
214     UCHAR* currentMapTable;
215     UINT bitPos;
216     void drawLine(const RegionRef& use, UINT x, UINT y, UCHAR colour, UINT len);
217     UCHAR get2Bits(const UCHAR* data, UINT& index);
218     UCHAR get4Bits(const UCHAR* data, UINT& index);
219     bool decode2BppCodeString(const UCHAR* data, UINT& index, const struct RegionRef& use, UINT& x, UINT y);
220     bool decode4BppCodeString(const UCHAR* data, UINT& index, const struct RegionRef& use, UINT& x, UINT y);
221     bool decode8BppCodeString(const UCHAR* data, UINT& index, const struct RegionRef& use, UINT& x, UINT y);
222   public:
223     DVBSubtitleObject(UINT id = 0);
224     void setNonModColourFlag(UCHAR flag) { nonModColourFlag = flag; }
225     void findWhereUsed(DVBSubtitlePage&);
226     void decodeSubBlock(const UCHAR* data, UINT length, bool even);
227 };
228
229 DVBSubtitleObject::DVBSubtitleObject(UINT id)
230 : objectID(id),
231   nonModColourFlag(0),
232   currentMapTable(NULL),
233   bitPos(0)
234 {
235   initMapTables();
236 }
237
238 void DVBSubtitleObject::initMapTables()
239 {
240   map2to4[0] = 0x0; map2to4[1] = 0x7; map2to4[2] = 0x8; map2to4[3] = 0xF;
241   map2to8[0] = 0x00; map2to8[1] = 0x77; map2to8[2] = 0x88; map2to8[3] = 0xFF;
242   map4to8[0] = 0x00; map4to8[1] = 0x11; map4to8[2] = 0x22; map4to8[3] = 0x33;
243   map4to8[4] = 0x44; map4to8[5] = 0x55; map4to8[6] = 0x66; map4to8[7] = 0x77;
244   map4to8[8] = 0x88; map4to8[9] = 0x99; map4to8[10]= 0xAA; map4to8[11]= 0xBB;
245   map4to8[12]= 0xCC; map4to8[13]= 0xDD; map4to8[14]= 0xEE; map4to8[15]= 0xFF;
246 }
247
248 void DVBSubtitleObject::findWhereUsed(DVBSubtitlePage& page)
249 {
250   useList.clear();
251   DVBSubtitlePage::RegionMap::iterator i;
252   for (i = page.regions.begin(); i != page.regions.end(); ++i)
253   {
254     DVBSubtitleRegion::ObjectList::iterator j;
255     for (j = i->second.objects.begin(); j != i->second.objects.end(); ++j)
256     {
257       if (j->objectID == objectID)
258       {
259         struct RegionRef usage;
260         usage.region = &(i->second);
261         usage.horizontalPosition = j->horizontalPosition;
262         usage.verticalPosition = j->verticalPosition;
263         useList.push_back(usage);
264       }
265     }
266   }
267 }
268
269 void DVBSubtitleObject::drawLine(const RegionRef& use, UINT x, UINT y,
270                                  UCHAR colour, UINT len)
271 {
272   if (nonModColourFlag && colour == 1) return;
273   x += use.horizontalPosition;
274   y += use.verticalPosition;
275   for (UINT pos = x; pos < x + len; ++pos)
276     use.region->setIndex(pos, y, colour);
277 }
278
279 UCHAR DVBSubtitleObject::get2Bits(const UCHAR* data, UINT& index)
280 {
281   UCHAR result = data[index];
282   if (!bitPos)
283   {
284     index++;
285     bitPos = 8;
286   }
287   bitPos -= 2;
288   return (result >> bitPos) & 0x03;
289 }
290
291 UCHAR DVBSubtitleObject::get4Bits(const UCHAR* data, UINT& index)
292 {
293   UCHAR result = data[index];
294   if (!bitPos)
295   {
296     index++;
297     bitPos = 4;
298   }
299   else
300   {
301     result >>= 4;
302     bitPos -= 4;
303   }
304   return result & 0x0F;
305 }
306
307 bool DVBSubtitleObject::decode2BppCodeString(const UCHAR* data, UINT& index, const struct RegionRef& use, UINT& x, UINT y)
308 {
309   UINT rl = 0, colour = 0;
310   UCHAR code = get2Bits(data, index);
311   if (code)
312   {
313     colour = code;
314     rl = 1;
315   }
316   else
317   {
318     code = get2Bits(data, index);
319     if (code & 2) // Switch 1
320     {
321       rl = ((code & 1) << 2) + get2Bits(data, index) + 3;
322       colour = get2Bits(data, index);
323     }
324     else if (code & 1) rl = 1; // Colour 0
325     else
326     {
327       code = get2Bits(data, index);
328       switch (code)
329       {
330         case 0:
331           return false; // end of 2-bit/pixel_code_string
332         case 1:
333           rl = 2; // Colour 0
334           break;
335         case 2:
336           rl = (get2Bits(data, index) << 2) + get2Bits(data, index) + 12;
337           colour = get2Bits(data, index);
338           break;
339         case 3:
340           rl = (get2Bits(data, index) << 6) + (get2Bits(data, index) << 4) +
341                (get2Bits(data, index) << 2) + get2Bits(data, index) + 29;
342           colour = get2Bits(data, index);
343           break;
344       }
345     }
346   }
347   drawLine(use, x, y, colour, rl);
348   x += rl;
349   return true;
350 }
351
352 bool DVBSubtitleObject::decode4BppCodeString(const UCHAR* data, UINT& index, const struct RegionRef& use, UINT& x, UINT y)
353 {
354   UINT rl = 0, colour = 0;
355   UCHAR code = get4Bits(data, index);
356   if (code)
357   {
358     colour = code;
359     rl = 1;
360   }
361   else
362   {
363     code = get4Bits(data, index);
364     if (code & 8) // Switch 1
365     {
366       if (code & 4) // Switch 2
367       {
368         switch (code & 3) // Switch 3
369         {
370           case 0: // Colour 0
371             rl = 1;
372             break;
373           case 1: // Colour 0
374             rl = 2;
375             break;
376           case 2:
377             rl = get4Bits(data, index) + 9;
378             colour = get4Bits(data, index);
379             break;
380           case 3:
381             rl = (get4Bits(data, index) << 4) + get4Bits(data, index) + 25;
382             colour = get4Bits(data, index);
383             break;
384         }
385       }
386       else
387       {
388         rl = (code & 3) + 4;
389         colour = get4Bits(data, index);
390       }
391     }
392     else
393     {
394       if (!code) return false; // end of 4-bit/pixel_code_string
395       rl = code + 2; // Colour 0
396     }
397   }
398   drawLine(use, x, y, colour, rl);
399   x += rl;
400   return true;
401 }
402
403 bool DVBSubtitleObject::decode8BppCodeString(const UCHAR* data, UINT& index, const struct RegionRef& use, UINT& x, UINT y)
404 {
405   UINT rl = 0, colour = 0;
406   UCHAR code = data[index++];
407   if (code)
408   {
409     colour = code;
410     rl = 1;
411   }
412   else
413   {
414     code = data[index++];
415     rl = code & 0x7F;
416     if (code & 0x80)
417       colour = data[index++];
418     else if (!rl)
419       return false; // else colour 0
420   }
421   drawLine(use, x, y, colour, rl);
422   x += rl;
423   return true;
424 }
425
426 void DVBSubtitleObject::decodeSubBlock(const UCHAR* data, UINT length, bool even)
427 {
428   UseList::const_iterator use;
429   for (use = useList.begin(); use != useList.end(); ++use)
430   {
431     UINT x = 0; UINT y = even ? 0 : 1; UINT index = 0;
432     while (index < length)
433     {
434       switch (data[index++])
435       {
436         case 0x10:
437           switch (use->region->palette.getBpp())
438           {
439             case 8: currentMapTable = map2to8; break;
440             case 4: currentMapTable = map2to4; break;
441             default: currentMapTable = NULL;
442           }
443           bitPos = 8;
444           while (decode2BppCodeString(data, index, *use,x,y) && index < length);
445           if (!bitPos) index++;
446           break;
447         case 0x11:
448           if (use->region->palette.getBpp() == 8)
449             currentMapTable = map4to8;
450           else
451             currentMapTable = NULL;
452           bitPos = 4;
453           while (decode4BppCodeString(data, index, *use,x,y) && index < length);
454           if (!bitPos) index++;
455           break;
456         case 0x12:
457           while (decode8BppCodeString(data, index, *use,x,y) && index < length);
458           break;
459         case 0x20:
460           map2to4[0] = data[index] >> 4;
461           map2to4[1] = data[index++] & 0x0F;
462           map2to4[2] = data[index] >> 4;
463           map2to4[3] = data[index++] & 0x0F;
464           break;
465         case 0x21:
466           for (int i = 0; i < 4; i++) map2to8[i] = data[index++];
467           break;
468         case 0x22:
469           for (int i = 0; i < 16; i++) map4to8[i] = data[index++];
470           break;
471         case 0xF0:
472           x = 0; y += 2;
473           break;
474       }
475     }
476   }
477 }
478
479 static ULLONG PTSDifference(ULLONG pts1, ULLONG pts2)
480 {
481   // Assume pts1, pts2 < 2^33; calculate pts1 - pts2
482   if (pts1 > pts2)
483     return pts1 - pts2;
484   else
485     return (1LL<<33) + pts1 - pts2;
486 }
487
488 DVBSubtitles::DVBSubtitles(OSDReceiver* tosd)
489 : osd(tosd),
490   pageOnDisplay(65536),
491   running(false),
492   showing(false),
493   threadNudged(false)
494 {
495 #ifndef WIN32
496   pthread_mutex_init(&input_mutex, NULL);
497   pthread_mutex_init(&output_mutex, NULL);
498 #else
499   input_mutex=CreateMutex(NULL,FALSE,NULL);
500   output_mutex=CreateMutex(NULL,FALSE,NULL);
501 #endif
502 }
503
504 void DVBSubtitles::put(const PESPacket& packet)
505 {
506   lockInput();
507   if (running)
508   {
509     if (packet.getPTS() != PESPacket::PTS_INVALID)
510     {
511       worklist.push_back(packet);
512       nudge();
513     }
514   }
515   unlockInput();
516 }
517
518 bool DVBSubtitles::decodePacket(const PESPacket& packet)
519 {
520   const UCHAR* data = packet.getData();
521   UINT packetSize = packet.getSize();
522   if (packetSize < 9) return false;
523   UINT segmentOffset = data[8] + 11;
524   while (packetSize > segmentOffset)
525   {
526     if (data[segmentOffset] == 0xFF) return true; // 'End of PES' marker
527     if (data[segmentOffset] != 0x0F) return false; // Out of sync
528     if (packetSize <= segmentOffset + 5) return false; // Packet truncated
529     UCHAR segmentType = data[segmentOffset + 1];
530     UINT pageID = (data[segmentOffset + 2] << 8)
531                  + data[segmentOffset + 3];
532     UINT segmentLength = (data[segmentOffset + 4] << 8)
533                         + data[segmentOffset + 5];
534     if (pageOnDisplay == 65536) pageOnDisplay = pageID;
535     if (pageOnDisplay != pageID) return false;
536     if (packetSize <= segmentOffset + 5 + segmentLength) return false;
537     const UCHAR* segmentData = &data[segmentOffset + 6];
538     pages[pageID]; // Create this page if it doesn't exist
539     PageMap::iterator pageEntry = pages.find(pageID);
540     if (pageEntry == pages.end()) return false;
541     DVBSubtitlePage& page = pageEntry->second;
542     if (packet.getPTS() != PESPacket::PTS_INVALID) page.pts = packet.getPTS();
543     switch (segmentType)
544     {
545     case 0x10: // Page composition
546     {
547       if (segmentLength < 2) break;
548       page.setState((segmentData[1] & 0x0C) >> 2);
549       if (!page.serviceAcquired) break;
550       UCHAR pageVersion = (segmentData[1] & 0xF0) >> 4;
551       if (pageVersion == page.version) break; // No update
552       page.version = pageVersion;
553       page.timeout = segmentData[0];
554       page.pendingDisplay.clear();
555       UINT parsePos = 2;
556       while (segmentLength > parsePos + 5)
557       {
558         UCHAR regionID = segmentData[parsePos];
559         page.regions[regionID]; // Create the region if it doesn't exist
560         DVBSubtitlePage::Coords coords;
561         coords.x = (segmentData[parsePos + 2] << 8) +
562                     segmentData[parsePos + 3];
563         coords.y = (segmentData[parsePos + 4] << 8) +
564                     segmentData[parsePos + 5];
565         page.pendingDisplay[regionID] = coords;
566         parsePos += 6;
567       }
568       break;
569     }
570     case 0x11: // Region composition
571     {
572       if (segmentLength < 10) break;
573       UCHAR regionID = segmentData[0];
574       page.regions[regionID]; // Create the region if it doesn't exist
575       DVBSubtitlePage::RegionMap::iterator regionEntry;
576       regionEntry = page.regions.find(regionID);
577       if (regionEntry == page.regions.end()) return false;
578       DVBSubtitleRegion& region = regionEntry->second;
579       UCHAR regionVersion = (segmentData[1] & 0xF0) >> 4;
580       if (regionVersion == region.version) break; // No update
581       region.version = regionVersion;
582       bool regionFillFlag = (segmentData[1] & 0x08) >> 3;
583       UINT regionWidth  = (segmentData[2] << 8) + segmentData[3];
584       UINT regionHeight = (segmentData[4] << 8) + segmentData[5];
585       region.setSize(regionWidth, regionHeight);
586       region.level = (segmentData[6] & 0xE0) >> 5;
587       region.setDepth((segmentData[6] & 0x1C) >> 2);
588       region.CLUTid = segmentData[7];
589       page.CLUTs[region.CLUTid]; // Create the CLUT if it doesn't exist
590       DVBSubtitlePage::CLUTMap::iterator clut;
591       clut = page.CLUTs.find(region.CLUTid);
592       if (clut == page.CLUTs.end()) return false;
593       region.palette = clut->second.getPalette(region.palette.getBpp());
594       if (regionFillFlag) switch (region.palette.getBpp())
595       {
596         case 2: region.setAllIndices((segmentData[9] & 0x0C) >> 2); break;
597         case 4: region.setAllIndices((segmentData[9] & 0xF0) >> 4); break;
598         case 8: region.setAllIndices(segmentData[8]); break;
599       }
600       region.objects.clear();
601       UINT parsePos = 10;
602       while (segmentLength > parsePos + 5)
603       {
604         UINT objectID = (segmentData[parsePos] << 8) +
605                          segmentData[parsePos + 1];
606         DVBSubtitleRegion::ObjectRef objref;
607         objref.objectID = objectID;
608         UINT objectType = (segmentData[parsePos + 2] & 0xC0) >> 6;
609         objref.horizontalPosition = ((segmentData[parsePos + 2] & 0x0F) << 8) +
610                                       segmentData[parsePos + 3];
611         objref.verticalPosition = ((segmentData[parsePos + 4] & 0x0F) << 8) +
612                                     segmentData[parsePos + 5];
613         if (objectType == 0) region.objects.push_back(objref);
614         parsePos += 6;
615       }
616       break; // case 0x11
617     }
618     case 0x12: // CLUT definition
619     {
620       if (segmentLength < 2) break;
621       UCHAR CLUTid = segmentData[0];
622       DVBSubtitlePage::CLUTMap::iterator clutEntry;
623       clutEntry = page.CLUTs.find(CLUTid);
624       if (clutEntry == page.CLUTs.end()) return false;
625       DVBSubtitleCLUT& clut = clutEntry->second;
626       clut.CLUTid = CLUTid;
627       UCHAR clutVersion = (segmentData[1] & 0xF0) >> 4;
628       if (clutVersion == clut.version) break; // No update
629       clut.version = clutVersion;
630       UINT parsePos = 2;
631       while (segmentLength > parsePos + 3)
632       {
633         UCHAR clutEntryID = segmentData[parsePos];
634         bool fullRangeFlag = segmentData[parsePos + 1] & 0x01;
635         UCHAR Y, Cr, Cb, T;
636         if (fullRangeFlag)
637         {
638           if (segmentLength <= parsePos + 5) break;
639           Y  = segmentData[parsePos + 2];
640           Cr = segmentData[parsePos + 3];
641           Cb = segmentData[parsePos + 4];
642           T  = segmentData[parsePos + 5];
643         }
644         else
645         {
646           Y   =  segmentData[parsePos + 2] & 0xFC;
647           Cr  = (segmentData[parsePos + 2] & 0x03) << 6;
648           Cr |= (segmentData[parsePos + 3] & 0xC0) >> 2;
649           Cb  = (segmentData[parsePos + 3] & 0x3C) << 2;
650           T   = (segmentData[parsePos + 3] & 0x03) << 6;
651         }
652         UCHAR entryFlags = segmentData[parsePos + 1];
653         if (entryFlags & 0x80) clut.setEntry(2, clutEntryID, Y,Cr,Cb,T);
654         if (entryFlags & 0x40) clut.setEntry(4, clutEntryID, Y,Cr,Cb,T);
655         if (entryFlags & 0x20) clut.setEntry(8, clutEntryID, Y,Cr,Cb,T);
656         parsePos += fullRangeFlag ? 6 : 4;
657       }
658       page.updateRegionPalettes(clut);
659       break; // case 0x12
660     }
661     case 0x13: // Object data
662     {
663       if (segmentLength < 3) break;
664       UINT objectID = (segmentData[0] << 8) + segmentData[1];
665       DVBSubtitleObject object(objectID);
666       object.findWhereUsed(page);
667       UCHAR codingMethod = (segmentData[2] & 0x0C) >> 2;
668       object.setNonModColourFlag(segmentData[2] & 0x01);
669       if (codingMethod == 0x00) // Coding of pixels
670       {
671         if (segmentLength < 7) break;
672         UINT topFieldLen     = (segmentData[3] << 8) + segmentData[4];
673         UINT bottomFieldLen  = (segmentData[5] << 8) + segmentData[6];
674         if (segmentLength < 7 + topFieldLen + bottomFieldLen) break;
675         object.decodeSubBlock(segmentData + 7, topFieldLen, true);
676         if (bottomFieldLen)
677           object.decodeSubBlock(segmentData + 7 + topFieldLen,
678                                 bottomFieldLen, false);
679         else
680           object.decodeSubBlock(segmentData + 7, topFieldLen, false);
681       }
682       else if (codingMethod == 0x01) // Coded as a string of characters
683         ; // TODO
684       break;
685     }
686     case 0x14: {// Display definition
687         if (segmentLength<5) break;
688         UINT ddsversion=(segmentData[0]&0xf0)>>4;
689         if (ddsversion==dds.version) break; // no update ncessary
690         dds.version=ddsversion;
691         dds.displaywindow=!(!(segmentData[0]&0x08));
692         dds.framewidth=(segmentData[1] << 8) + segmentData[2];
693         dds.frameheight=(segmentData[3] << 8) + segmentData[4];
694         if (segmentLength<13) break;
695         if (dds.displaywindow) {
696                 dds.windowx=(segmentData[4] << 8) + segmentData[5];
697                 dds.windoww=(segmentData[6] << 8) + segmentData[7]-dds.windowx;
698                 dds.windowy=(segmentData[8] << 8) + segmentData[9];
699                 dds.windowh=(segmentData[10] << 8) + segmentData[11]-dds.windowy;
700         } else {
701                 dds.windowx=0;
702                 dds.windowy=0;
703                 dds.windoww=dds.framewidth;
704                 dds.windowh=dds.frameheight;
705         }
706       break;
707     }
708     case 0x80: // End of display set
709       finishPage(page);
710       page.dirty = false;
711       page.currentDisplay = page.pendingDisplay;
712       break;
713     }
714     segmentOffset += (segmentLength + 6);
715   }
716   return false; // Fall through from while loop: we ran out of data
717 }
718
719 void DVBSubtitles::finishPage(const DVBSubtitlePage& page)
720 {
721   if (page.dirty)
722   {
723     osd->clearOSD();
724   }
725
726   DVBSubtitlePage::DisplayMap::const_iterator i,j;
727   for (i = page.currentDisplay.begin(); i != page.currentDisplay.end(); ++i)
728   { // For each currently displayed region, if the region has been
729     // moved or removed, blank out its previous screen position
730     j = page.pendingDisplay.find(i->first);
731     if (j == page.pendingDisplay.end() ||
732         j->second.x != i->second.x ||
733         j->second.y != i->second.y)
734     {
735       DVBSubtitlePage::RegionMap::const_iterator region_iter;
736       region_iter = page.regions.find(i->first);
737       if (region_iter == page.regions.end()) continue;
738       Log::getInstance()->log("SUBTITLES", Log::DEBUG, "Clear region %d", i->first);
739       osd->clearOSDArea(i->second.x, i->second.y,
740            region_iter->second.getWidth(), region_iter->second.getHeight(),dds);
741     }
742   }
743
744   for (i = page.pendingDisplay.begin(); i != page.pendingDisplay.end(); ++i)
745   { // Display each pending region
746     DVBSubtitlePage::RegionMap::const_iterator region_iter;
747     region_iter = page.regions.find(i->first);
748     if (region_iter == page.regions.end()) continue;
749     Log::getInstance()->log("SUBTITLES", Log::DEBUG, "Display region %d", i->first);
750     osd->drawOSDBitmap(i->second.x, i->second.y, region_iter->second,dds);
751   }
752 }
753
754 void DVBSubtitles::lockInput()
755 {
756 #ifndef WIN32
757   pthread_mutex_lock(&input_mutex);
758 #else
759   WaitForSingleObject(input_mutex, INFINITE);
760 #endif
761 }
762
763 void DVBSubtitles::unlockInput()
764 {
765 #ifndef WIN32
766   pthread_mutex_unlock(&input_mutex);
767 #else
768   ReleaseMutex(input_mutex);
769 #endif
770 }
771
772 void DVBSubtitles::lockOutput()
773 {
774 #ifndef WIN32
775   pthread_mutex_lock(&output_mutex);
776 #else
777   WaitForSingleObject(output_mutex, INFINITE);
778 #endif
779 }
780
781 void DVBSubtitles::unlockOutput()
782 {
783 #ifndef WIN32
784   pthread_mutex_unlock(&output_mutex);
785 #else
786   ReleaseMutex(output_mutex);
787 #endif
788 }
789
790 int DVBSubtitles::start()
791 {
792   lockInput();
793   dds=DVBSubtitleDisplayDefinition();
794   running = true;
795   unlockInput();
796   return threadStart();
797 }
798
799 void DVBSubtitles::stop()
800 {
801   threadStop();
802   lockInput();
803   running = false;
804   worklist.clear();
805   unlockInput();
806   lockOutput();
807   PageMap::const_iterator pageEntry = pages.find(pageOnDisplay);
808   if (pageEntry != pages.end())
809   {
810     const DVBSubtitlePage& page = pageEntry->second;
811     DVBSubtitlePage::DisplayMap::const_iterator i;
812     for (i = page.currentDisplay.begin(); i != page.currentDisplay.end(); ++i)
813     {
814       DVBSubtitlePage::RegionMap::const_iterator region_iter;
815       region_iter = page.regions.find(i->first);
816       if (region_iter == page.regions.end()) continue;
817       osd->clearOSDArea(i->second.x, i->second.y,
818            region_iter->second.getWidth(), region_iter->second.getHeight(),dds);
819     }
820   }
821   pages.clear();
822   pageOnDisplay = 65536;
823   unlockOutput();
824   threadNudged = false;
825 }
826
827 void DVBSubtitles::show()
828 {
829   lockOutput();
830   showing = true;
831   unlockOutput();
832 }
833
834 void DVBSubtitles::hide()
835 {
836   lockOutput();
837   showing = false;
838   PageMap::const_iterator pageEntry = pages.find(pageOnDisplay);
839   if (pageEntry != pages.end())
840   {
841     const DVBSubtitlePage& page = pageEntry->second;
842     DVBSubtitlePage::DisplayMap::const_iterator i;
843     for (i = page.currentDisplay.begin(); i != page.currentDisplay.end(); ++i)
844     {
845       DVBSubtitlePage::RegionMap::const_iterator region_iter;
846       region_iter = page.regions.find(i->first);
847       if (region_iter == page.regions.end()) continue;
848       osd->clearOSDArea(i->second.x, i->second.y,
849            region_iter->second.getWidth(), region_iter->second.getHeight(),dds);
850     }
851   }
852   pages.clear();
853   pageOnDisplay = 65536;
854   unlockOutput();
855 }
856
857 void DVBSubtitles::nudge()
858 {
859 #ifndef WIN32
860   pthread_mutex_lock(&threadCondMutex);
861   threadNudged = true;
862   pthread_cond_signal(&threadCond);
863   pthread_mutex_unlock(&threadCondMutex);
864 #else
865   WaitForSingleObject(threadCondMutex, INFINITE);
866   threadNudged = true;
867   SetEvent(threadCond);
868   ReleaseMutex(threadCondMutex);
869 #endif
870 }
871
872 void DVBSubtitles::threadMethod()
873 {
874   struct timespec sleeptime;
875   sleeptime.tv_sec = 0;
876   sleeptime.tv_nsec = 0;
877   while (1)
878   {
879     threadCheckExit();
880     threadLock();
881     if (!threadNudged)
882     { // We have done the current work and no more has arrived. Sleep.
883       if (sleeptime.tv_sec == 0 && sleeptime.tv_nsec == 0)
884       {
885         Log::getInstance()->log("SUBTITLES", Log::DEBUG, "Sleeping until nudged.");
886         threadWaitForSignal();
887       }
888       else
889       {
890         Log::getInstance()->log("SUBTITLES", Log::DEBUG, "Sleeping for %d and %d", sleeptime.tv_sec, sleeptime.tv_nsec);
891         struct timespec targetTime;
892 #ifndef WIN32
893         clock_gettime(CLOCK_REALTIME, &targetTime);
894 #else
895         SYSTEMTIME systime;
896         __int64  filetime;
897         __int64  test;
898         GetSystemTime(&systime);
899         SystemTimeToFileTime(&systime,(FILETIME*)&filetime);
900         targetTime.tv_sec=(filetime-WINDOWS_TIME_BASE_OFFSET)/(10*1000*1000);
901         targetTime.tv_nsec=((filetime-WINDOWS_TIME_BASE_OFFSET)%(10*1000*1000))*100;
902 #endif
903         targetTime.tv_nsec += sleeptime.tv_nsec;
904         if (targetTime.tv_nsec > 999999999)
905         {
906           targetTime.tv_sec += 1;
907           targetTime.tv_nsec -= 1000000000;
908         }
909         targetTime.tv_sec += sleeptime.tv_sec;
910         threadWaitForSignalTimed(&targetTime);
911       }
912     }
913     threadNudged = false;
914     threadUnlock();
915     bool finished = false;
916     ULLONG nowPTS, pktPTS;
917     while (!finished)
918     {
919       threadCheckExit();
920       finished = true;
921       pktPTS = PESPacket::PTS_INVALID;
922       lockInput();
923       if (!worklist.empty())
924         pktPTS = worklist.front().getPTS();
925       unlockInput();
926       if (pktPTS != PESPacket::PTS_INVALID)
927       { // An entry exists in the work list
928         nowPTS = Video::getInstance()->getCurrentTimestamp();
929         if (PTSDifference(nowPTS, pktPTS) < 2*90000 ||
930             PTSDifference(pktPTS, nowPTS) < 4000)
931         { // It is due for processing or discarding.
932           finished = false;
933           lockInput();
934           if (!worklist.empty())
935           {
936             PESPacket packet = worklist.front();
937             worklist.pop_front();
938             unlockInput();
939             lockOutput();
940             if (showing) decodePacket(packet);
941             unlockOutput();
942           }
943           else unlockInput();
944         }
945         else if (PTSDifference(pktPTS, nowPTS) >= 60*90000)
946         { // Seems like a bad or very old entry. Get rid of it.
947           finished = false;
948           lockInput();
949           if (!worklist.empty()) worklist.pop_front();
950           unlockInput();
951         }
952       }
953     }
954     // Calculate how long to sleep for
955     sleeptime.tv_sec = 0;
956     sleeptime.tv_nsec = 0;
957     if (pktPTS != PESPacket::PTS_INVALID)
958     { // A future action exists
959       nowPTS = Video::getInstance()->getCurrentTimestamp();
960       ULLONG diff = PTSDifference(pktPTS, nowPTS);
961       sleeptime.tv_sec = diff / 90000;
962       sleeptime.tv_nsec = (long)(diff % 90000) * 10000L / 90000L * 100000L;
963       if (sleeptime.tv_sec > 60)
964       { // We have a problem. An action so far in the future should have
965         // been culled. Probably the action is already due and PTSDifference
966         // wrapped around. Therefore we sleep for a minimal time instead.
967         sleeptime.tv_sec = 0;
968         sleeptime.tv_nsec = 1;
969       }
970     }
971   }
972 }