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