]> git.vomp.tv Git - vompclient.git/blob - dvbsubtitles.cc
Vogel Media Player 2008-11-28
[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 class DVBSubtitleObject
192 {
193   private:
194     UINT objectID;
195     UCHAR nonModColourFlag;
196     struct RegionRef
197     {
198       DVBSubtitleRegion* region;
199       UINT horizontalPosition;
200       UINT verticalPosition;
201     };
202     typedef std::vector<struct RegionRef> UseList;
203     UseList useList;
204     UCHAR map2to4[4];
205     UCHAR map2to8[4];
206     UCHAR map4to8[16];
207     void initMapTables();
208     UCHAR* currentMapTable;
209     UINT bitPos;
210     void drawLine(const RegionRef& use, UINT x, UINT y, UCHAR colour, UINT len);
211     UCHAR get2Bits(const UCHAR* data, UINT& index);
212     UCHAR get4Bits(const UCHAR* data, UINT& index);
213     bool decode2BppCodeString(const UCHAR* data, UINT& index, const struct RegionRef& use, UINT& x, UINT y);
214     bool decode4BppCodeString(const UCHAR* data, UINT& index, const struct RegionRef& use, UINT& x, UINT y);
215     bool decode8BppCodeString(const UCHAR* data, UINT& index, const struct RegionRef& use, UINT& x, UINT y);
216   public:
217     DVBSubtitleObject(UINT id = 0);
218     void setNonModColourFlag(UCHAR flag) { nonModColourFlag = flag; }
219     void findWhereUsed(DVBSubtitlePage&);
220     void decodeSubBlock(const UCHAR* data, UINT length, bool even);
221 };
222
223 DVBSubtitleObject::DVBSubtitleObject(UINT id)
224 : objectID(id),
225   nonModColourFlag(0),
226   currentMapTable(NULL),
227   bitPos(0)
228 {
229   initMapTables();
230 }
231
232 void DVBSubtitleObject::initMapTables()
233 {
234   map2to4[0] = 0x0; map2to4[1] = 0x7; map2to4[2] = 0x8; map2to4[3] = 0xF;
235   map2to8[0] = 0x00; map2to8[1] = 0x77; map2to8[2] = 0x88; map2to8[3] = 0xFF;
236   map4to8[0] = 0x00; map4to8[1] = 0x11; map4to8[2] = 0x22; map4to8[3] = 0x33;
237   map4to8[4] = 0x44; map4to8[5] = 0x55; map4to8[6] = 0x66; map4to8[7] = 0x77;
238   map4to8[8] = 0x88; map4to8[9] = 0x99; map4to8[10]= 0xAA; map4to8[11]= 0xBB;
239   map4to8[12]= 0xCC; map4to8[13]= 0xDD; map4to8[14]= 0xEE; map4to8[15]= 0xFF;
240 }
241
242 void DVBSubtitleObject::findWhereUsed(DVBSubtitlePage& page)
243 {
244   useList.clear();
245   DVBSubtitlePage::RegionMap::iterator i;
246   for (i = page.regions.begin(); i != page.regions.end(); ++i)
247   {
248     DVBSubtitleRegion::ObjectList::iterator j;
249     for (j = i->second.objects.begin(); j != i->second.objects.end(); ++j)
250     {
251       if (j->objectID == objectID)
252       {
253         struct RegionRef usage;
254         usage.region = &(i->second);
255         usage.horizontalPosition = j->horizontalPosition;
256         usage.verticalPosition = j->verticalPosition;
257         useList.push_back(usage);
258       }
259     }
260   }
261 }
262
263 void DVBSubtitleObject::drawLine(const RegionRef& use, UINT x, UINT y,
264                                  UCHAR colour, UINT len)
265 {
266   if (nonModColourFlag && colour == 1) return;
267   x += use.horizontalPosition;
268   y += use.verticalPosition;
269   for (UINT pos = x; pos < x + len; ++pos)
270     use.region->setIndex(pos, y, colour);
271 }
272
273 UCHAR DVBSubtitleObject::get2Bits(const UCHAR* data, UINT& index)
274 {
275   UCHAR result = data[index];
276   if (!bitPos)
277   {
278     index++;
279     bitPos = 8;
280   }
281   bitPos -= 2;
282   return (result >> bitPos) & 0x03;
283 }
284
285 UCHAR DVBSubtitleObject::get4Bits(const UCHAR* data, UINT& index)
286 {
287   UCHAR result = data[index];
288   if (!bitPos)
289   {
290     index++;
291     bitPos = 4;
292   }
293   else
294   {
295     result >>= 4;
296     bitPos -= 4;
297   }
298   return result & 0x0F;
299 }
300
301 bool DVBSubtitleObject::decode2BppCodeString(const UCHAR* data, UINT& index, const struct RegionRef& use, UINT& x, UINT y)
302 {
303   UINT rl = 0, colour = 0;
304   UCHAR code = get2Bits(data, index);
305   if (code)
306   {
307     colour = code;
308     rl = 1;
309   }
310   else
311   {
312     code = get2Bits(data, index);
313     if (code & 2) // Switch 1
314     {
315       rl = ((code & 1) << 2) + get2Bits(data, index) + 3;
316       colour = get2Bits(data, index);
317     }
318     else if (code & 1) rl = 1; // Colour 0
319     else
320     {
321       code = get2Bits(data, index);
322       switch (code)
323       {
324         case 0:
325           return false; // end of 2-bit/pixel_code_string
326         case 1:
327           rl = 2; // Colour 0
328           break;
329         case 2:
330           rl = (get2Bits(data, index) << 2) + get2Bits(data, index) + 12;
331           colour = get2Bits(data, index);
332           break;
333         case 3:
334           rl = (get2Bits(data, index) << 6) + (get2Bits(data, index) << 4) +
335                (get2Bits(data, index) << 2) + get2Bits(data, index) + 29;
336           colour = get2Bits(data, index);
337           break;
338       }
339     }
340   }
341   drawLine(use, x, y, colour, rl);
342   x += rl;
343   return true;
344 }
345
346 bool DVBSubtitleObject::decode4BppCodeString(const UCHAR* data, UINT& index, const struct RegionRef& use, UINT& x, UINT y)
347 {
348   UINT rl = 0, colour = 0;
349   UCHAR code = get4Bits(data, index);
350   if (code)
351   {
352     colour = code;
353     rl = 1;
354   }
355   else
356   {
357     code = get4Bits(data, index);
358     if (code & 8) // Switch 1
359     {
360       if (code & 4) // Switch 2
361       {
362         switch (code & 3) // Switch 3
363         {
364           case 0: // Colour 0
365             rl = 1;
366             break;
367           case 1: // Colour 0
368             rl = 2;
369             break;
370           case 2:
371             rl = get4Bits(data, index) + 9;
372             colour = get4Bits(data, index);
373             break;
374           case 3:
375             rl = (get4Bits(data, index) << 4) + get4Bits(data, index) + 25;
376             colour = get4Bits(data, index);
377             break;
378         }
379       }
380       else
381       {
382         rl = (code & 3) + 4;
383         colour = get4Bits(data, index);
384       }
385     }
386     else
387     {
388       if (!code) return false; // end of 4-bit/pixel_code_string
389       rl = code + 2; // Colour 0
390     }
391   }
392   drawLine(use, x, y, colour, rl);
393   x += rl;
394   return true;
395 }
396
397 bool DVBSubtitleObject::decode8BppCodeString(const UCHAR* data, UINT& index, const struct RegionRef& use, UINT& x, UINT y)
398 {
399   UINT rl = 0, colour = 0;
400   UCHAR code = data[index++];
401   if (code)
402   {
403     colour = code;
404     rl = 1;
405   }
406   else
407   {
408     code = data[index++];
409     rl = code & 0x7F;
410     if (code & 0x80)
411       colour = data[index++];
412     else if (!rl)
413       return false; // else colour 0
414   }
415   drawLine(use, x, y, colour, rl);
416   x += rl;
417   return true;
418 }
419
420 void DVBSubtitleObject::decodeSubBlock(const UCHAR* data, UINT length, bool even)
421 {
422   UseList::const_iterator use;
423   for (use = useList.begin(); use != useList.end(); ++use)
424   {
425     UINT x = 0; UINT y = even ? 0 : 1; UINT index = 0;
426     while (index < length)
427     {
428       switch (data[index++])
429       {
430         case 0x10:
431           switch (use->region->palette.getBpp())
432           {
433             case 8: currentMapTable = map2to8; break;
434             case 4: currentMapTable = map2to4; break;
435             default: currentMapTable = NULL;
436           }
437           bitPos = 8;
438           while (decode2BppCodeString(data, index, *use,x,y) && index < length);
439           if (!bitPos) index++;
440           break;
441         case 0x11:
442           if (use->region->palette.getBpp() == 8)
443             currentMapTable = map4to8;
444           else
445             currentMapTable = NULL;
446           bitPos = 4;
447           while (decode4BppCodeString(data, index, *use,x,y) && index < length);
448           if (!bitPos) index++;
449           break;
450         case 0x12:
451           while (decode8BppCodeString(data, index, *use,x,y) && index < length);
452           break;
453         case 0x20:
454           map2to4[0] = data[index] >> 4;
455           map2to4[1] = data[index++] & 0x0F;
456           map2to4[2] = data[index] >> 4;
457           map2to4[3] = data[index++] & 0x0F;
458           break;
459         case 0x21:
460           for (int i = 0; i < 4; i++) map2to8[i] = data[index++];
461           break;
462         case 0x22:
463           for (int i = 0; i < 16; i++) map4to8[i] = data[index++];
464           break;
465         case 0xF0:
466           x = 0; y += 2;
467           break;
468       }
469     }
470   }
471 }
472
473 static ULLONG PTSDifference(ULLONG pts1, ULLONG pts2)
474 {
475   // Assume pts1, pts2 < 2^33; calculate pts1 - pts2
476   if (pts1 > pts2)
477     return pts1 - pts2;
478   else
479     return (1LL<<33) + pts1 - pts2;
480 }
481
482 DVBSubtitles::DVBSubtitles(OSDReceiver* tosd)
483 : osd(tosd),
484   pageOnDisplay(65536),
485   running(false),
486   showing(false),
487   threadNudged(false)
488 {
489 #ifndef WIN32
490   pthread_mutex_init(&input_mutex, NULL);
491   pthread_mutex_init(&output_mutex, NULL);
492 #else
493   input_mutex=CreateMutex(NULL,FALSE,NULL);
494   output_mutex=CreateMutex(NULL,FALSE,NULL);
495 #endif
496 }
497
498 void DVBSubtitles::put(const PESPacket& packet)
499 {
500   lockInput();
501   if (running)
502   {
503     if (packet.getPTS() != PESPacket::PTS_INVALID)
504     {
505       worklist.push_back(packet);
506       nudge();
507     }
508   }
509   unlockInput();
510 }
511
512 bool DVBSubtitles::decodePacket(const PESPacket& packet)
513 {
514   const UCHAR* data = packet.getData();
515   UINT packetSize = packet.getSize();
516   if (packetSize < 9) return false;
517   UINT segmentOffset = data[8] + 11;
518   while (packetSize > segmentOffset)
519   {
520     if (data[segmentOffset] == 0xFF) return true; // 'End of PES' marker
521     if (data[segmentOffset] != 0x0F) return false; // Out of sync
522     if (packetSize <= segmentOffset + 5) return false; // Packet truncated
523     UCHAR segmentType = data[segmentOffset + 1];
524     UINT pageID = (data[segmentOffset + 2] << 8)
525                  + data[segmentOffset + 3];
526     UINT segmentLength = (data[segmentOffset + 4] << 8)
527                         + data[segmentOffset + 5];
528     if (pageOnDisplay == 65536) pageOnDisplay = pageID;
529     if (pageOnDisplay != pageID) return false;
530     if (packetSize <= segmentOffset + 5 + segmentLength) return false;
531     const UCHAR* segmentData = &data[segmentOffset + 6];
532     pages[pageID]; // Create this page if it doesn't exist
533     PageMap::iterator pageEntry = pages.find(pageID);
534     if (pageEntry == pages.end()) return false;
535     DVBSubtitlePage& page = pageEntry->second;
536     if (packet.getPTS() != PESPacket::PTS_INVALID) page.pts = packet.getPTS();
537     switch (segmentType)
538     {
539     case 0x10: // Page composition
540     {
541       if (segmentLength < 2) break;
542       page.setState((segmentData[1] & 0x0C) >> 2);
543       if (!page.serviceAcquired) break;
544       UCHAR pageVersion = (segmentData[1] & 0xF0) >> 4;
545       if (pageVersion == page.version) break; // No update
546       page.version = pageVersion;
547       page.timeout = segmentData[0];
548       page.pendingDisplay.clear();
549       UINT parsePos = 2;
550       while (segmentLength > parsePos + 5)
551       {
552         UCHAR regionID = segmentData[parsePos];
553         page.regions[regionID]; // Create the region if it doesn't exist
554         DVBSubtitlePage::Coords coords;
555         coords.x = (segmentData[parsePos + 2] << 8) +
556                     segmentData[parsePos + 3];
557         coords.y = (segmentData[parsePos + 4] << 8) +
558                     segmentData[parsePos + 5];
559         page.pendingDisplay[regionID] = coords;
560         parsePos += 6;
561       }
562       break;
563     }
564     case 0x11: // Region composition
565     {
566       if (segmentLength < 10) break;
567       UCHAR regionID = segmentData[0];
568       page.regions[regionID]; // Create the region if it doesn't exist
569       DVBSubtitlePage::RegionMap::iterator regionEntry;
570       regionEntry = page.regions.find(regionID);
571       if (regionEntry == page.regions.end()) return false;
572       DVBSubtitleRegion& region = regionEntry->second;
573       UCHAR regionVersion = (segmentData[1] & 0xF0) >> 4;
574       if (regionVersion == region.version) break; // No update
575       region.version = regionVersion;
576       bool regionFillFlag = (segmentData[1] & 0x08) >> 3;
577       UINT regionWidth  = (segmentData[2] << 8) + segmentData[3];
578       UINT regionHeight = (segmentData[4] << 8) + segmentData[5];
579       region.setSize(regionWidth, regionHeight);
580       region.level = (segmentData[6] & 0xE0) >> 5;
581       region.setDepth((segmentData[6] & 0x1C) >> 2);
582       region.CLUTid = segmentData[7];
583       page.CLUTs[region.CLUTid]; // Create the CLUT if it doesn't exist
584       DVBSubtitlePage::CLUTMap::iterator clut;
585       clut = page.CLUTs.find(region.CLUTid);
586       if (clut == page.CLUTs.end()) return false;
587       region.palette = clut->second.getPalette(region.palette.getBpp());
588       if (regionFillFlag) switch (region.palette.getBpp())
589       {
590         case 2: region.setAllIndices((segmentData[9] & 0x0C) >> 2); break;
591         case 4: region.setAllIndices((segmentData[9] & 0xF0) >> 4); break;
592         case 8: region.setAllIndices(segmentData[8]); break;
593       }
594       region.objects.clear();
595       UINT parsePos = 10;
596       while (segmentLength > parsePos + 5)
597       {
598         UINT objectID = (segmentData[parsePos] << 8) +
599                          segmentData[parsePos + 1];
600         DVBSubtitleRegion::ObjectRef objref;
601         objref.objectID = objectID;
602         UINT objectType = (segmentData[parsePos + 2] & 0xC0) >> 6;
603         objref.horizontalPosition = ((segmentData[parsePos + 2] & 0x0F) << 8) +
604                                       segmentData[parsePos + 3];
605         objref.verticalPosition = ((segmentData[parsePos + 4] & 0x0F) << 8) +
606                                     segmentData[parsePos + 5];
607         if (objectType == 0) region.objects.push_back(objref);
608         parsePos += 6;
609       }
610       break; // case 0x11
611     }
612     case 0x12: // CLUT definition
613     {
614       if (segmentLength < 2) break;
615       UCHAR CLUTid = segmentData[0];
616       DVBSubtitlePage::CLUTMap::iterator clutEntry;
617       clutEntry = page.CLUTs.find(CLUTid);
618       if (clutEntry == page.CLUTs.end()) return false;
619       DVBSubtitleCLUT& clut = clutEntry->second;
620       clut.CLUTid = CLUTid;
621       UCHAR clutVersion = (segmentData[1] & 0xF0) >> 4;
622       if (clutVersion == clut.version) break; // No update
623       clut.version = clutVersion;
624       UINT parsePos = 2;
625       while (segmentLength > parsePos + 3)
626       {
627         UCHAR clutEntryID = segmentData[parsePos];
628         bool fullRangeFlag = segmentData[parsePos + 1] & 0x01;
629         UCHAR Y, Cr, Cb, T;
630         if (fullRangeFlag)
631         {
632           if (segmentLength <= parsePos + 5) break;
633           Y  = segmentData[parsePos + 2];
634           Cr = segmentData[parsePos + 3];
635           Cb = segmentData[parsePos + 4];
636           T  = segmentData[parsePos + 5];
637         }
638         else
639         {
640           Y   =  segmentData[parsePos + 2] & 0xFC;
641           Cr  = (segmentData[parsePos + 2] & 0x03) << 6;
642           Cr |= (segmentData[parsePos + 3] & 0xC0) >> 2;
643           Cb  = (segmentData[parsePos + 3] & 0x3C) << 2;
644           T   = (segmentData[parsePos + 3] & 0x03) << 6;
645         }
646         UCHAR entryFlags = segmentData[parsePos + 1];
647         if (entryFlags & 0x80) clut.setEntry(2, clutEntryID, Y,Cr,Cb,T);
648         if (entryFlags & 0x40) clut.setEntry(4, clutEntryID, Y,Cr,Cb,T);
649         if (entryFlags & 0x20) clut.setEntry(8, clutEntryID, Y,Cr,Cb,T);
650         parsePos += fullRangeFlag ? 6 : 4;
651       }
652       page.updateRegionPalettes(clut);
653       break; // case 0x12
654     }
655     case 0x13: // Object data
656     {
657       if (segmentLength < 3) break;
658       UINT objectID = (segmentData[0] << 8) + segmentData[1];
659       DVBSubtitleObject object(objectID);
660       object.findWhereUsed(page);
661       UCHAR codingMethod = (segmentData[2] & 0x0C) >> 2;
662       object.setNonModColourFlag(segmentData[2] & 0x01);
663       if (codingMethod == 0x00) // Coding of pixels
664       {
665         if (segmentLength < 7) break;
666         UINT topFieldLen     = (segmentData[3] << 8) + segmentData[4];
667         UINT bottomFieldLen  = (segmentData[5] << 8) + segmentData[6];
668         if (segmentLength < 7 + topFieldLen + bottomFieldLen) break;
669         object.decodeSubBlock(segmentData + 7, topFieldLen, true);
670         if (bottomFieldLen)
671           object.decodeSubBlock(segmentData + 7 + topFieldLen,
672                                 bottomFieldLen, false);
673         else
674           object.decodeSubBlock(segmentData + 7, topFieldLen, false);
675       }
676       else if (codingMethod == 0x01) // Coded as a string of characters
677         ; // TODO
678       break;
679     }
680     case 0x14: // Display definition
681       break; // TODO: Ignore now and assume 720x576 per ETSI EN 300 743 V1.2.1
682     case 0x80: // End of display set
683       finishPage(page);
684       page.dirty = false;
685       page.currentDisplay = page.pendingDisplay;
686       break;
687     }
688     segmentOffset += (segmentLength + 6);
689   }
690   return false; // Fall through from while loop: we ran out of data
691 }
692
693 void DVBSubtitles::finishPage(const DVBSubtitlePage& page)
694 {
695   if (page.dirty)
696   {
697     osd->clearOSD();
698   }
699
700   DVBSubtitlePage::DisplayMap::const_iterator i,j;
701   for (i = page.currentDisplay.begin(); i != page.currentDisplay.end(); ++i)
702   { // For each currently displayed region, if the region has been
703     // moved or removed, blank out its previous screen position
704     j = page.pendingDisplay.find(i->first);
705     if (j == page.pendingDisplay.end() ||
706         j->second.x != i->second.x ||
707         j->second.y != i->second.y)
708     {
709       DVBSubtitlePage::RegionMap::const_iterator region_iter;
710       region_iter = page.regions.find(i->first);
711       if (region_iter == page.regions.end()) continue;
712       Log::getInstance()->log("SUBTITLES", Log::DEBUG, "Clear region %d", i->first);
713       osd->clearOSDArea(i->second.x, i->second.y,
714            region_iter->second.getWidth(), region_iter->second.getHeight());
715     }
716   }
717
718   for (i = page.pendingDisplay.begin(); i != page.pendingDisplay.end(); ++i)
719   { // Display each pending region
720     DVBSubtitlePage::RegionMap::const_iterator region_iter;
721     region_iter = page.regions.find(i->first);
722     if (region_iter == page.regions.end()) continue;
723     Log::getInstance()->log("SUBTITLES", Log::DEBUG, "Display region %d", i->first);
724     osd->drawOSDBitmap(i->second.x, i->second.y, region_iter->second);
725   }
726 }
727
728 void DVBSubtitles::lockInput()
729 {
730 #ifndef WIN32
731   pthread_mutex_lock(&input_mutex);
732 #else
733   WaitForSingleObject(input_mutex, INFINITE);
734 #endif
735 }
736
737 void DVBSubtitles::unlockInput()
738 {
739 #ifndef WIN32
740   pthread_mutex_unlock(&input_mutex);
741 #else
742   ReleaseMutex(input_mutex);
743 #endif
744 }
745
746 void DVBSubtitles::lockOutput()
747 {
748 #ifndef WIN32
749   pthread_mutex_lock(&output_mutex);
750 #else
751   WaitForSingleObject(output_mutex, INFINITE);
752 #endif
753 }
754
755 void DVBSubtitles::unlockOutput()
756 {
757 #ifndef WIN32
758   pthread_mutex_unlock(&output_mutex);
759 #else
760   ReleaseMutex(output_mutex);
761 #endif
762 }
763
764 int DVBSubtitles::start()
765 {
766   lockInput();
767   running = true;
768   unlockInput();
769   return threadStart();
770 }
771
772 void DVBSubtitles::stop()
773 {
774   threadStop();
775   lockInput();
776   running = false;
777   worklist.clear();
778   unlockInput();
779   lockOutput();
780   PageMap::const_iterator pageEntry = pages.find(pageOnDisplay);
781   if (pageEntry != pages.end())
782   {
783     const DVBSubtitlePage& page = pageEntry->second;
784     DVBSubtitlePage::DisplayMap::const_iterator i;
785     for (i = page.currentDisplay.begin(); i != page.currentDisplay.end(); ++i)
786     {
787       DVBSubtitlePage::RegionMap::const_iterator region_iter;
788       region_iter = page.regions.find(i->first);
789       if (region_iter == page.regions.end()) continue;
790       osd->clearOSDArea(i->second.x, i->second.y,
791            region_iter->second.getWidth(), region_iter->second.getHeight());
792     }
793   }
794   pages.clear();
795   pageOnDisplay = 65536;
796   unlockOutput();
797   threadNudged = false;
798 }
799
800 void DVBSubtitles::show()
801 {
802   lockOutput();
803   showing = true;
804   unlockOutput();
805 }
806
807 void DVBSubtitles::hide()
808 {
809   lockOutput();
810   showing = false;
811   PageMap::const_iterator pageEntry = pages.find(pageOnDisplay);
812   if (pageEntry != pages.end())
813   {
814     const DVBSubtitlePage& page = pageEntry->second;
815     DVBSubtitlePage::DisplayMap::const_iterator i;
816     for (i = page.currentDisplay.begin(); i != page.currentDisplay.end(); ++i)
817     {
818       DVBSubtitlePage::RegionMap::const_iterator region_iter;
819       region_iter = page.regions.find(i->first);
820       if (region_iter == page.regions.end()) continue;
821       osd->clearOSDArea(i->second.x, i->second.y,
822            region_iter->second.getWidth(), region_iter->second.getHeight());
823     }
824   }
825   pages.clear();
826   pageOnDisplay = 65536;
827   unlockOutput();
828 }
829
830 void DVBSubtitles::nudge()
831 {
832 #ifndef WIN32
833   pthread_mutex_lock(&threadCondMutex);
834   threadNudged = true;
835   pthread_cond_signal(&threadCond);
836   pthread_mutex_unlock(&threadCondMutex);
837 #else
838   WaitForSingleObject(threadCondMutex, INFINITE);
839   threadNudged = true;
840   SetEvent(threadCond);
841   ReleaseMutex(threadCondMutex);
842 #endif
843 }
844
845 void DVBSubtitles::threadMethod()
846 {
847   struct timespec sleeptime;
848   sleeptime.tv_sec = 0;
849   sleeptime.tv_nsec = 0;
850   while (1)
851   {
852     threadCheckExit();
853     threadLock();
854     if (!threadNudged)
855     { // We have done the current work and no more has arrived. Sleep.
856       if (sleeptime.tv_sec == 0 && sleeptime.tv_nsec == 0)
857       {
858         Log::getInstance()->log("SUBTITLES", Log::DEBUG, "Sleeping until nudged.");
859         threadWaitForSignal();
860       }
861       else
862       {
863         Log::getInstance()->log("SUBTITLES", Log::DEBUG, "Sleeping for %d and %d", sleeptime.tv_sec, sleeptime.tv_nsec);
864         struct timespec targetTime;
865 #ifndef WIN32
866         clock_gettime(CLOCK_REALTIME, &targetTime);
867 #else
868         SYSTEMTIME systime;
869         __int64  filetime;
870         __int64  test;
871         GetSystemTime(&systime);
872         SystemTimeToFileTime(&systime,(FILETIME*)&filetime);
873         targetTime.tv_sec=(filetime-WINDOWS_TIME_BASE_OFFSET)/(10*1000*1000);
874         targetTime.tv_nsec=((filetime-WINDOWS_TIME_BASE_OFFSET)%(10*1000*1000))*100;
875 #endif
876         targetTime.tv_nsec += sleeptime.tv_nsec;
877         if (targetTime.tv_nsec > 999999999)
878         {
879           targetTime.tv_sec += 1;
880           targetTime.tv_nsec -= 1000000000;
881         }
882         targetTime.tv_sec += sleeptime.tv_sec;
883         threadWaitForSignalTimed(&targetTime);
884       }
885     }
886     threadNudged = false;
887     threadUnlock();
888     bool finished = false;
889     ULLONG nowPTS, pktPTS;
890     while (!finished)
891     {
892       threadCheckExit();
893       finished = true;
894       pktPTS = PESPacket::PTS_INVALID;
895       lockInput();
896       if (!worklist.empty())
897         pktPTS = worklist.front().getPTS();
898       unlockInput();
899       if (pktPTS != PESPacket::PTS_INVALID)
900       { // An entry exists in the work list
901         nowPTS = Video::getInstance()->getCurrentTimestamp();
902         if (PTSDifference(nowPTS, pktPTS) < 2*90000 ||
903             PTSDifference(pktPTS, nowPTS) < 4000)
904         { // It is due for processing or discarding.
905           finished = false;
906           lockInput();
907           if (!worklist.empty())
908           {
909             PESPacket packet = worklist.front();
910             worklist.pop_front();
911             unlockInput();
912             lockOutput();
913             if (showing) decodePacket(packet);
914             unlockOutput();
915           }
916           else unlockInput();
917         }
918         else if (PTSDifference(pktPTS, nowPTS) >= 60*90000)
919         { // Seems like a bad or very old entry. Get rid of it.
920           finished = false;
921           lockInput();
922           if (!worklist.empty()) worklist.pop_front();
923           unlockInput();
924         }
925       }
926     }
927     // Calculate how long to sleep for
928     sleeptime.tv_sec = 0;
929     sleeptime.tv_nsec = 0;
930     if (pktPTS != PESPacket::PTS_INVALID)
931     { // A future action exists
932       nowPTS = Video::getInstance()->getCurrentTimestamp();
933       ULLONG diff = PTSDifference(pktPTS, nowPTS);
934       sleeptime.tv_sec = diff / 90000;
935       sleeptime.tv_nsec = (long)(diff % 90000) * 10000L / 90000L * 100000L;
936       if (sleeptime.tv_sec > 60)
937       { // We have a problem. An action so far in the future should have
938         // been culled. Probably the action is already due and PTSDifference
939         // wrapped around. Therefore we sleep for a minimal time instead.
940         sleeptime.tv_sec = 0;
941         sleeptime.tv_nsec = 1;
942       }
943     }
944   }
945 }