]> git.vomp.tv Git - vompclient.git/blob - dvbsubtitles.cc
WIP: DVBSubtitles new timing loop, std::thread/cond and std::chrono
[vompclient.git] / dvbsubtitles.cc
1 /*
2     Copyright 2008 Mark Calderbank, 2020 Chris Tallon
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, see <https://www.gnu.org/licenses/>.
18 */
19 /*  Sections of code and general methods adopted from VDR version 1.6.0
20  *  dvbsubtitle.c (GPL version 2 or later)
21  *  Original author: Marco Schluessler <marco@lordzodiac.de>
22  *  With some input from the "subtitle plugin"
23  *  by Pekka Virtanen <pekka.virtanen@sci.fi>
24 */
25
26 /*  Reference ETSI EN 300 743 V1.3.1 (2006-11) */
27
28
29 #define DVBSDEBUG 0
30
31
32 #ifdef DVBSDEBUG
33
34 // DBG
35 #include <stdio.h>
36 #include <string>
37 #include <chrono>
38 #include <iomanip>
39
40 std::string tp2str(const std::chrono::time_point<std::chrono::system_clock>& tp)
41 {
42   auto tms = std::chrono::time_point_cast<std::chrono::milliseconds>(tp);
43   std::chrono::milliseconds e = tms.time_since_epoch();
44   long long c = e.count();
45   time_t tt = c / 1000;
46   int ttm = c % 1000;
47   auto stm = std::localtime(&tt);
48   std::stringstream ss;
49   ss << std::put_time(stm, "%T") << "." << std::setfill('0') << std::setw(3) << ttm;
50   return ss.str();
51 }
52
53 #endif
54
55
56
57
58 #include "demuxer.h"
59 #include "osdreceiver.h"
60 #include "video.h"
61 #include "log.h"
62
63 #include "dvbsubtitles.h"
64
65 DVBSubtitleCLUT::DVBSubtitleCLUT()
66 : version(0xFF),
67   palette2(2),
68   palette4(4),
69   palette8(8)
70 {
71   for (int i = 0; i < 4; ++i) palette2.setColour(i, defaultPalette2[i]);
72   for (int i = 0; i < 16; ++i) palette4.setColour(i, defaultPalette4[i]);
73   for (int i = 0; i < 256; ++i) palette8.setColour(i, defaultPalette8[i]);
74 }
75
76 const ULONG DVBSubtitleCLUT::defaultPalette2[4] = {
77   0x00000000, 0xFFFFFFFF, 0xFF000000, 0xFF808080
78 };
79
80 const ULONG DVBSubtitleCLUT::defaultPalette4[16] = {
81   0x00000000, 0xFFFF0000, 0xFF00FF00, 0xFFFFFF00,
82   0xFF0000FF, 0xFFFF00FF, 0xFF00FFFF, 0xFFFFFFFF,
83   0xFF000000, 0xFF800000, 0xFF008000, 0xFF808000,
84   0xFF000080, 0xFF800080, 0xFF008080, 0xFF808080
85 };
86
87 const ULONG DVBSubtitleCLUT::defaultPalette8[256] = {
88   0x00000000, 0x40FF0000, 0x4000FF00, 0x40FFFF00, // 0000 0000
89   0x400000FF, 0x40FF00FF, 0x4000FFFF, 0x40FFFFFF, // 0000 0100
90   0x80000000, 0x80550000, 0x80005500, 0x80555500, // 0000 1000
91   0x80000055, 0x80550055, 0x80005555, 0x80555555, // 0000 1100
92   0xFFAA0000, 0xFFFF0000, 0xFFAA5500, 0xFFFF5500, // 0001 0000
93   0xFFAA0055, 0xFFFF0055, 0xFFAA5555, 0xFFFF5555, // 0001 0100
94   0x80AA0000, 0x80FF0000, 0x80AA5500, 0x80FF5500, // 0001 1000
95   0x80AA0055, 0x80FF0055, 0x80AA5555, 0x80FF5555, // 0001 1100
96   0xFF00AA00, 0xFF55AA00, 0xFF00FF00, 0xFF55FF00, // 0010 0000
97   0xFF00AA55, 0xFF55AA55, 0xFF00FF55, 0xFF55FF55, // 0010 0100
98   0x8000AA00, 0x8055AA00, 0x8000FF00, 0x8055FF00, // 0010 1000
99   0x8000AA55, 0x8055AA55, 0x8000FF55, 0x8055FF55, // 0010 1100
100   0xFFAAAA00, 0xFFFFAA00, 0xFFAAFF00, 0xFFFFFF00, // 0011 0000
101   0xFFAAAA55, 0xFFFFAA55, 0xFFAAFF55, 0xFFFFFF55, // 0011 0100
102   0x80AAAA00, 0x80FFAA00, 0x80AAFF00, 0x80FFFF00, // 0011 1000
103   0x80AAAA55, 0x80FFAA55, 0x80AAFF55, 0x80FFFF55, // 0011 1100
104   0xFF0000AA, 0xFF5500AA, 0xFF0055AA, 0xFF5555AA, // 0100 0000
105   0xFF0000FF, 0xFF5500FF, 0xFF0055FF, 0xFF5555FF, // 0100 0100
106   0x800000AA, 0x805500AA, 0x800055AA, 0x805555AA, // 0100 1000
107   0x800000FF, 0x805500FF, 0x800055FF, 0x805555FF, // 0100 1100
108   0xFFAA00AA, 0xFFFF00AA, 0xFFAA55AA, 0xFFFF55AA, // 0101 0000
109   0xFFAA00FF, 0xFFFF00FF, 0xFFAA55FF, 0xFFFF55FF, // 0101 0100
110   0x80AA00AA, 0x80FF00AA, 0x80AA55AA, 0x80FF55AA, // 0101 1000
111   0x80AA00FF, 0x80FF00FF, 0x80AA55FF, 0x80FF55FF, // 0101 1100
112   0xFF00AAAA, 0xFF55AAAA, 0xFF00FFAA, 0xFF55FFAA, // 0110 0000
113   0xFF00AAFF, 0xFF55AAFF, 0xFF00FFFF, 0xFF55FFFF, // 0110 0100
114   0x8000AAAA, 0x8055AAAA, 0x8000FFAA, 0x8055FFAA, // 0110 1000
115   0x8000AAFF, 0x8055AAFF, 0x8000FFFF, 0x8055FFFF, // 0110 1100
116   0xFFAAAAAA, 0xFFFFAAAA, 0xFFAAFFAA, 0xFFFFFFAA, // 0111 0000
117   0xFFAAAAFF, 0xFFFFAAFF, 0xFFAAFFFF, 0xFFFFFFFF, // 0111 0100
118   0x80AAAAAA, 0x80FFAAAA, 0x80AAFFAA, 0x80FFFFAA, // 0111 1000
119   0x80AAAAFF, 0x80FFAAFF, 0x80AAFFFF, 0x80FFFFFF, // 0111 1100
120   0xFF808080, 0xFFAA8080, 0xFF80AA80, 0xFFAAAA80, // 1000 0000
121   0xFF8080AA, 0xFFAA80AA, 0xFF80AAAA, 0xFFAAAAAA, // 1000 0100
122   0xFF000000, 0xFF2A0000, 0xFF002A00, 0xFF2A2A00, // 1000 1000
123   0xFF00002A, 0xFF2A002A, 0xFF002A2A, 0xFF2A2A2A, // 1000 1100
124   0xFFD58080, 0xFFFF8080, 0xFFD5AA80, 0xFFFFAA80, // 1001 0000
125   0xFFD580AA, 0xFFFF80AA, 0xFFD5AAAA, 0xFFFFAAAA, // 1001 0100
126   0xFF550000, 0xFF7F0000, 0xFF552A00, 0xFF7F2A00, // 1001 1000
127   0xFF55002A, 0xFF7F002A, 0xFF552A2A, 0xFF7F2A2A, // 1001 1100
128   0xFF80D580, 0xFFAAD580, 0xFF80FF80, 0xFFAAFF80, // 1010 0000
129   0xFF80D5AA, 0xFFAAD5AA, 0xFF80FFAA, 0xFFAAFFAA, // 1010 0100
130   0xFF005500, 0xFF2A5500, 0xFF007F00, 0xFF2A7F00, // 1010 1000
131   0xFF00552A, 0xFF2A552A, 0xFF007F2A, 0xFF2A7F2A, // 1010 1100
132   0xFFD5D580, 0xFFFFD580, 0xFFD5FF80, 0xFFFFFF80, // 1011 0000
133   0xFFD5D5AA, 0xFFFFD5AA, 0xFFD5FFAA, 0xFFFFFFAA, // 1011 0100
134   0xFF555500, 0xFF7F5500, 0xFF557F00, 0xFF7F7F00, // 1011 1000
135   0xFF55552A, 0xFF7F552A, 0xFF557F2A, 0xFF7F7F2A, // 1011 1100
136   0xFF8080D5, 0xFFAA80D5, 0xFF80AAD5, 0xFFAAAAD5, // 1100 0000
137   0xFF8080FF, 0xFFAA80FF, 0xFF80AAFF, 0xFFAAAAFF, // 1100 0100
138   0xFF000055, 0xFF2A0055, 0xFF002A55, 0xFF2A2A55, // 1100 1000
139   0xFF00007F, 0xFF2A007F, 0xFF002A7F, 0xFF2A2A7F, // 1100 1100
140   0xFFD580D5, 0xFFFF80D5, 0xFFD5AAD5, 0xFFFFAAD5, // 1101 0000
141   0xFFD580FF, 0xFFFF80FF, 0xFFD5AAFF, 0xFFFFAAFF, // 1101 0100
142   0xFF550055, 0xFF7F0055, 0xFF552A55, 0xFF7F2A55, // 1101 1000
143   0xFF55007F, 0xFF7F007F, 0xFF552A7F, 0xFF7F2A7F, // 1101 1100
144   0xFF80D5D5, 0xFFAAD5D5, 0xFF80FFD5, 0xFFAAFFD5, // 1110 0000
145   0xFF80D5FF, 0xFFAAD5FF, 0xFF80FFFF, 0xFFAAFFFF, // 1110 0100
146   0xFF005555, 0xFF2A5555, 0xFF007F55, 0xFF2A7F55, // 1110 1000
147   0xFF00557F, 0xFF2A557F, 0xFF007F7F, 0xFF2A7F7F, // 1110 1100
148   0xFFD5D5D5, 0xFFFFD5D5, 0xFFD5FFD5, 0xFFFFFFD5, // 1111 0000
149   0xFFD5D5FF, 0xFFFFD5FF, 0xFFD5FFFF, 0xFFFFFFFF, // 1111 0100
150   0xFF555555, 0xFF7F5555, 0xFF557F55, 0xFF7F7F55, // 1111 1000
151   0xFF55557F, 0xFF7F557F, 0xFF557F7F, 0xFF7F7F7F, // 1111 1100
152 };
153
154 void DVBSubtitleCLUT::setEntry(int bpp, UINT index, UCHAR Y, UCHAR Cr, UCHAR Cb, UCHAR T)
155 {
156   UCHAR Y2 = Y;
157   UCHAR A;
158   if (Y2 == 0) { Y2 = 16; A = 0; } else A = 255 - T;
159   switch (bpp)
160   {
161     case 2: palette2.setYCrCbA(index, Y2, Cr, Cb, A); break;
162     case 4: palette4.setYCrCbA(index, Y2, Cr, Cb, A); break;
163     case 8: palette8.setYCrCbA(index, Y2, Cr, Cb, A); break;
164   }
165 }
166
167 const Palette& DVBSubtitleCLUT::getPalette(int bpp) const
168 {
169   switch (bpp)
170   {
171     case 2: return palette2; break;
172     case 4: return palette4; break;
173     default: return palette8; break;
174   }
175 }
176
177 DVBSubtitleRegion::DVBSubtitleRegion()
178 : version(0xFF),
179   level(0),
180   CLUTid(0)
181 {}
182
183 void DVBSubtitleRegion::setDepth(UINT d)
184 {
185   if (d < 4) palette.setBpp(1 << d);
186 }
187
188 DVBSubtitlePage::DVBSubtitlePage()
189 : pts(0),
190   dirty(false),
191   version(0xFF),
192   timeout(0),
193   state(0),
194   serviceAcquired(false)
195 {}
196
197 void DVBSubtitlePage::setState(UCHAR s)
198 {
199   state = s;
200   if (state == 1) // Acquisition point - page refresh
201   if (!serviceAcquired) // Service not acquired lets treat this as mode change
202      state=2;
203   if (state == 2) // Mode change - new epoch
204   {
205     serviceAcquired = true;
206     dirty = true;
207     regions.clear();
208     currentDisplay.clear();
209     pendingDisplay.clear();
210     CLUTs.clear();
211   }
212 }
213
214 void DVBSubtitlePage::updateRegionPalettes(const DVBSubtitleCLUT& clut)
215 {
216   RegionMap::iterator region;
217   for (region = regions.begin(); region != regions.end(); ++region)
218     if (region->second.CLUTid == clut.CLUTid)
219       region->second.palette = clut.getPalette(region->second.palette.getBpp());
220 }
221
222 DVBSubtitleDisplayDefinition::DVBSubtitleDisplayDefinition()
223 {
224         version=0xFF;
225         displaywindow=false;
226 }
227
228 class DVBSubtitleObject
229 {
230   private:
231     UINT objectID;
232     UCHAR nonModColourFlag;
233     struct RegionRef
234     {
235       DVBSubtitleRegion* region;
236       UINT horizontalPosition;
237       UINT verticalPosition;
238     };
239     typedef std::vector<struct RegionRef> UseList;
240     UseList useList;
241     UCHAR map2to4[4];
242     UCHAR map2to8[4];
243     UCHAR map4to8[16];
244     void initMapTables();
245     UCHAR* currentMapTable;
246     UINT bitPos;
247     void drawLine(const RegionRef& use, UINT x, UINT y, UCHAR colour, UINT len);
248     UCHAR get2Bits(const UCHAR* data, UINT& index);
249     UCHAR get4Bits(const UCHAR* data, UINT& index);
250     bool decode2BppCodeString(const UCHAR* data, UINT& index, const struct RegionRef& use, UINT& x, UINT y);
251     bool decode4BppCodeString(const UCHAR* data, UINT& index, const struct RegionRef& use, UINT& x, UINT y);
252     bool decode8BppCodeString(const UCHAR* data, UINT& index, const struct RegionRef& use, UINT& x, UINT y);
253   public:
254     DVBSubtitleObject(UINT id = 0);
255     void setNonModColourFlag(UCHAR flag) { nonModColourFlag = flag; }
256     void findWhereUsed(DVBSubtitlePage&);
257     void decodeSubBlock(const UCHAR* data, UINT length, bool even);
258 };
259
260 DVBSubtitleObject::DVBSubtitleObject(UINT id)
261 : objectID(id),
262   nonModColourFlag(0),
263   currentMapTable(NULL),
264   bitPos(0)
265 {
266   initMapTables();
267 }
268
269 void DVBSubtitleObject::initMapTables()
270 {
271   map2to4[0] = 0x0; map2to4[1] = 0x7; map2to4[2] = 0x8; map2to4[3] = 0xF;
272   map2to8[0] = 0x00; map2to8[1] = 0x77; map2to8[2] = 0x88; map2to8[3] = 0xFF;
273   map4to8[0] = 0x00; map4to8[1] = 0x11; map4to8[2] = 0x22; map4to8[3] = 0x33;
274   map4to8[4] = 0x44; map4to8[5] = 0x55; map4to8[6] = 0x66; map4to8[7] = 0x77;
275   map4to8[8] = 0x88; map4to8[9] = 0x99; map4to8[10]= 0xAA; map4to8[11]= 0xBB;
276   map4to8[12]= 0xCC; map4to8[13]= 0xDD; map4to8[14]= 0xEE; map4to8[15]= 0xFF;
277 }
278
279 void DVBSubtitleObject::findWhereUsed(DVBSubtitlePage& page)
280 {
281   useList.clear();
282   DVBSubtitlePage::RegionMap::iterator i;
283   for (i = page.regions.begin(); i != page.regions.end(); ++i)
284   {
285     DVBSubtitleRegion::ObjectList::iterator j;
286     for (j = i->second.objects.begin(); j != i->second.objects.end(); ++j)
287     {
288       if (j->objectID == objectID)
289       {
290         struct RegionRef usage;
291         usage.region = &(i->second);
292         usage.horizontalPosition = j->horizontalPosition;
293         usage.verticalPosition = j->verticalPosition;
294         useList.push_back(usage);
295       }
296     }
297   }
298 }
299
300 void DVBSubtitleObject::drawLine(const RegionRef& use, UINT x, UINT y,
301                                  UCHAR colour, UINT len)
302 {
303   if (nonModColourFlag && colour == 1) return;
304   x += use.horizontalPosition;
305   y += use.verticalPosition;
306   for (UINT pos = x; pos < x + len; ++pos)
307     use.region->setIndex(pos, y, colour);
308 }
309
310 UCHAR DVBSubtitleObject::get2Bits(const UCHAR* data, UINT& index)
311 {
312   UCHAR result = data[index];
313   if (!bitPos)
314   {
315     index++;
316     bitPos = 8;
317   }
318   bitPos -= 2;
319   return (result >> bitPos) & 0x03;
320 }
321
322 UCHAR DVBSubtitleObject::get4Bits(const UCHAR* data, UINT& index)
323 {
324   UCHAR result = data[index];
325   if (!bitPos)
326   {
327     index++;
328     bitPos = 4;
329   }
330   else
331   {
332     result >>= 4;
333     bitPos -= 4;
334   }
335   return result & 0x0F;
336 }
337
338 bool DVBSubtitleObject::decode2BppCodeString(const UCHAR* data, UINT& index, const struct RegionRef& use, UINT& x, UINT y)
339 {
340   UINT rl = 0, colour = 0;
341   UCHAR code = get2Bits(data, index);
342   if (code)
343   {
344     colour = code;
345     rl = 1;
346   }
347   else
348   {
349     code = get2Bits(data, index);
350     if (code & 2) // Switch 1
351     {
352       rl = ((code & 1) << 2) + get2Bits(data, index) + 3;
353       colour = get2Bits(data, index);
354     }
355     else if (code & 1) rl = 1; // Colour 0
356     else
357     {
358       code = get2Bits(data, index);
359       switch (code)
360       {
361         case 0:
362           return false; // end of 2-bit/pixel_code_string
363         case 1:
364           rl = 2; // Colour 0
365           break;
366         case 2:
367           rl = (get2Bits(data, index) << 2) + get2Bits(data, index) + 12;
368           colour = get2Bits(data, index);
369           break;
370         case 3:
371           rl = (get2Bits(data, index) << 6) + (get2Bits(data, index) << 4) +
372                (get2Bits(data, index) << 2) + get2Bits(data, index) + 29;
373           colour = get2Bits(data, index);
374           break;
375       }
376     }
377   }
378   drawLine(use, x, y, colour, rl);
379   x += rl;
380   return true;
381 }
382
383 bool DVBSubtitleObject::decode4BppCodeString(const UCHAR* data, UINT& index, const struct RegionRef& use, UINT& x, UINT y)
384 {
385   UINT rl = 0, colour = 0;
386   UCHAR code = get4Bits(data, index);
387   if (code)
388   {
389     colour = code;
390     rl = 1;
391   }
392   else
393   {
394     code = get4Bits(data, index);
395     if (code & 8) // Switch 1
396     {
397       if (code & 4) // Switch 2
398       {
399         switch (code & 3) // Switch 3
400         {
401           case 0: // Colour 0
402             rl = 1;
403             break;
404           case 1: // Colour 0
405             rl = 2;
406             break;
407           case 2:
408             rl = get4Bits(data, index) + 9;
409             colour = get4Bits(data, index);
410             break;
411           case 3:
412             rl = (get4Bits(data, index) << 4) + get4Bits(data, index) + 25;
413             colour = get4Bits(data, index);
414             break;
415         }
416       }
417       else
418       {
419         rl = (code & 3) + 4;
420         colour = get4Bits(data, index);
421       }
422     }
423     else
424     {
425       if (!code) return false; // end of 4-bit/pixel_code_string
426       rl = code + 2; // Colour 0
427     }
428   }
429   drawLine(use, x, y, colour, rl);
430   x += rl;
431   return true;
432 }
433
434 bool DVBSubtitleObject::decode8BppCodeString(const UCHAR* data, UINT& index, const struct RegionRef& use, UINT& x, UINT y)
435 {
436   UINT rl = 0, colour = 0;
437   UCHAR code = data[index++];
438   if (code)
439   {
440     colour = code;
441     rl = 1;
442   }
443   else
444   {
445     code = data[index++];
446     rl = code & 0x7F;
447     if (code & 0x80)
448       colour = data[index++];
449     else if (!rl)
450       return false; // else colour 0
451   }
452   drawLine(use, x, y, colour, rl);
453   x += rl;
454   return true;
455 }
456
457 void DVBSubtitleObject::decodeSubBlock(const UCHAR* data, UINT length, bool even)
458 {
459   UseList::const_iterator use;
460   for (use = useList.begin(); use != useList.end(); ++use)
461   {
462     UINT x = 0; UINT y = even ? 0 : 1; UINT index = 0;
463     while (index < length)
464     {
465       switch (data[index++])
466       {
467         case 0x10:
468           switch (use->region->palette.getBpp())
469           {
470             case 8: currentMapTable = map2to8; break;
471             case 4: currentMapTable = map2to4; break;
472             default: currentMapTable = NULL;
473           }
474           bitPos = 8;
475           while (decode2BppCodeString(data, index, *use,x,y) && index < length);
476           if (!bitPos) index++;
477           break;
478         case 0x11:
479           if (use->region->palette.getBpp() == 8)
480             currentMapTable = map4to8;
481           else
482             currentMapTable = NULL;
483           bitPos = 4;
484           while (decode4BppCodeString(data, index, *use,x,y) && index < length);
485           if (!bitPos) index++;
486           break;
487         case 0x12:
488           while (decode8BppCodeString(data, index, *use,x,y) && index < length);
489           break;
490         case 0x20:
491           map2to4[0] = data[index] >> 4;
492           map2to4[1] = data[index++] & 0x0F;
493           map2to4[2] = data[index] >> 4;
494           map2to4[3] = data[index++] & 0x0F;
495           break;
496         case 0x21:
497           for (int i = 0; i < 4; i++) map2to8[i] = data[index++];
498           break;
499         case 0x22:
500           for (int i = 0; i < 16; i++) map4to8[i] = data[index++];
501           break;
502         case 0xF0:
503           x = 0; y += 2;
504           break;
505       }
506     }
507   }
508 }
509
510 static ULLONG PTSDifference(ULLONG pts1, ULLONG pts2)
511 {
512   // Assume pts1, pts2 < 2^33; calculate pts1 - pts2
513   if (pts1 > pts2)
514     return pts1 - pts2;
515   else
516     return (1LL<<33) + pts1 - pts2;
517 }
518
519 DVBSubtitles::DVBSubtitles(OSDReceiver* tosd)
520 : osd(tosd),
521   pageOnDisplay(65536),
522   running(false),
523   showing(false)
524 {
525 }
526
527 void DVBSubtitles::put(const PESPacket& packet)
528 {
529   // Player object play feed thread comes here, via the demuxer
530   // input_mutex only used in put() here, start(), stop() and threadMethod()
531   // So, roll old Thread::mutex/cond into input_mutex?
532
533   input_mutex.lock();
534   if (running)
535   {
536     if (packet.getPTS() != PESPacket::PTS_INVALID)
537     {
538       worklist.push_back(packet);
539       signalRecalcWLTO = true;
540         Log::getInstance()->log("DVBSubs", Log::DEBUG, "Received packet: PTS: %llu, size: %u, type: %u", packet.getPTS(), packet.getSize(), packet.getPacketType());
541
542
543       dvbsCond.notify_one();
544     }
545     else
546     {
547         Log::getInstance()->log("DVBSubs", Log::DEBUG, "PUT DROPPING INVALID PACKET: PTS: %llu, size: %u, type: %u", packet.getPTS(), packet.getSize(), packet.getPacketType());
548     }
549   }
550   input_mutex.unlock();
551 }
552
553 bool DVBSubtitles::decodePacket(const PESPacket& packet)
554 {
555   const UCHAR* data = packet.getData();
556   UINT packetSize = packet.getSize();
557   if (packetSize < 9) return false;
558   UINT segmentOffset = data[8] + 11;
559   while (packetSize > segmentOffset)
560   {
561     if (data[segmentOffset] == 0xFF) return true; // 'End of PES' marker
562     if (data[segmentOffset] != 0x0F) return false; // Out of sync
563     if (packetSize <= segmentOffset + 5) return false; // Packet truncated
564     UCHAR segmentType = data[segmentOffset + 1];
565     UINT pageID = (data[segmentOffset + 2] << 8)
566                  + data[segmentOffset + 3];
567     UINT segmentLength = (data[segmentOffset + 4] << 8)
568                         + data[segmentOffset + 5];
569     if (pageOnDisplay == 65536) pageOnDisplay = pageID;
570     if (pageOnDisplay != pageID) return false;
571     if (packetSize <= segmentOffset + 5 + segmentLength) return false;
572     const UCHAR* segmentData = &data[segmentOffset + 6];
573     pages[pageID]; // Create this page if it doesn't exist
574     PageMap::iterator pageEntry = pages.find(pageID);
575     if (pageEntry == pages.end()) return false;
576     DVBSubtitlePage& page = pageEntry->second;
577     if (packet.getPTS() != PESPacket::PTS_INVALID) page.pts = packet.getPTS();
578     switch (segmentType)
579     {
580     case 0x10: // Page composition
581     {
582       if (segmentLength < 2) break;
583       page.setState((segmentData[1] & 0x0C) >> 2);
584       if (!page.serviceAcquired) break;
585       UCHAR pageVersion = (segmentData[1] & 0xF0) >> 4;
586       if (pageVersion == page.version) break; // No update
587       page.version = pageVersion;
588       page.timeout = segmentData[0];
589       page.pendingDisplay.clear();
590       UINT parsePos = 2;
591       while (segmentLength > parsePos + 5)
592       {
593         UCHAR regionID = segmentData[parsePos];
594         page.regions[regionID]; // Create the region if it doesn't exist
595         DVBSubtitlePage::Coords coords;
596         coords.x = (segmentData[parsePos + 2] << 8) +
597                     segmentData[parsePos + 3];
598         coords.y = (segmentData[parsePos + 4] << 8) +
599                     segmentData[parsePos + 5];
600         page.pendingDisplay[regionID] = coords;
601         parsePos += 6;
602       }
603       break;
604     }
605     case 0x11: // Region composition
606     {
607       if (segmentLength < 10) break;
608       UCHAR regionID = segmentData[0];
609       page.regions[regionID]; // Create the region if it doesn't exist
610       DVBSubtitlePage::RegionMap::iterator regionEntry;
611       regionEntry = page.regions.find(regionID);
612       if (regionEntry == page.regions.end()) return false;
613       DVBSubtitleRegion& region = regionEntry->second;
614       UCHAR regionVersion = (segmentData[1] & 0xF0) >> 4;
615       if (regionVersion == region.version) break; // No update
616       region.version = regionVersion;
617       bool regionFillFlag = (segmentData[1] & 0x08) >> 3;
618       UINT regionWidth  = (segmentData[2] << 8) + segmentData[3];
619       UINT regionHeight = (segmentData[4] << 8) + segmentData[5];
620       region.setSize(regionWidth, regionHeight);
621       region.level = (segmentData[6] & 0xE0) >> 5;
622       region.setDepth((segmentData[6] & 0x1C) >> 2);
623       region.CLUTid = segmentData[7];
624       page.CLUTs[region.CLUTid]; // Create the CLUT if it doesn't exist
625       DVBSubtitlePage::CLUTMap::iterator clut;
626       clut = page.CLUTs.find(region.CLUTid);
627       if (clut == page.CLUTs.end()) return false;
628       region.palette = clut->second.getPalette(region.palette.getBpp());
629       if (regionFillFlag) switch (region.palette.getBpp())
630       {
631         case 2: region.setAllIndices((segmentData[9] & 0x0C) >> 2); break;
632         case 4: region.setAllIndices((segmentData[9] & 0xF0) >> 4); break;
633         case 8: region.setAllIndices(segmentData[8]); break;
634       }
635       region.objects.clear();
636       UINT parsePos = 10;
637       while (segmentLength > parsePos + 5)
638       {
639         UINT objectID = (segmentData[parsePos] << 8) +
640                          segmentData[parsePos + 1];
641         DVBSubtitleRegion::ObjectRef objref;
642         objref.objectID = objectID;
643         UINT objectType = (segmentData[parsePos + 2] & 0xC0) >> 6;
644         objref.horizontalPosition = ((segmentData[parsePos + 2] & 0x0F) << 8) +
645                                       segmentData[parsePos + 3];
646         objref.verticalPosition = ((segmentData[parsePos + 4] & 0x0F) << 8) +
647                                     segmentData[parsePos + 5];
648         if (objectType == 0) region.objects.push_back(objref);
649         parsePos += 6;
650       }
651       break; // case 0x11
652     }
653     case 0x12: // CLUT definition
654     {
655       if (segmentLength < 2) break;
656       UCHAR CLUTid = segmentData[0];
657       DVBSubtitlePage::CLUTMap::iterator clutEntry;
658       clutEntry = page.CLUTs.find(CLUTid);
659       if (clutEntry == page.CLUTs.end()) return false;
660       DVBSubtitleCLUT& clut = clutEntry->second;
661       clut.CLUTid = CLUTid;
662       UCHAR clutVersion = (segmentData[1] & 0xF0) >> 4;
663       if (clutVersion == clut.version) break; // No update
664       clut.version = clutVersion;
665       UINT parsePos = 2;
666       while (segmentLength > parsePos + 3)
667       {
668         UCHAR clutEntryID = segmentData[parsePos];
669         bool fullRangeFlag = segmentData[parsePos + 1] & 0x01;
670         UCHAR Y, Cr, Cb, T;
671         if (fullRangeFlag)
672         {
673           if (segmentLength <= parsePos + 5) break;
674           Y  = segmentData[parsePos + 2];
675           Cr = segmentData[parsePos + 3];
676           Cb = segmentData[parsePos + 4];
677           T  = segmentData[parsePos + 5];
678         }
679         else
680         {
681           Y   =  segmentData[parsePos + 2] & 0xFC;
682           Cr  = (segmentData[parsePos + 2] & 0x03) << 6;
683           Cr |= (segmentData[parsePos + 3] & 0xC0) >> 2;
684           Cb  = (segmentData[parsePos + 3] & 0x3C) << 2;
685           T   = (segmentData[parsePos + 3] & 0x03) << 6;
686         }
687         UCHAR entryFlags = segmentData[parsePos + 1];
688         if (entryFlags & 0x80) clut.setEntry(2, clutEntryID, Y,Cr,Cb,T);
689         if (entryFlags & 0x40) clut.setEntry(4, clutEntryID, Y,Cr,Cb,T);
690         if (entryFlags & 0x20) clut.setEntry(8, clutEntryID, Y,Cr,Cb,T);
691         parsePos += fullRangeFlag ? 6 : 4;
692       }
693       page.updateRegionPalettes(clut);
694       break; // case 0x12
695     }
696     case 0x13: // Object data
697     {
698       if (segmentLength < 3) break;
699       UINT objectID = (segmentData[0] << 8) + segmentData[1];
700       DVBSubtitleObject object(objectID);
701       object.findWhereUsed(page);
702       UCHAR codingMethod = (segmentData[2] & 0x0C) >> 2;
703       object.setNonModColourFlag(segmentData[2] & 0x01);
704       if (codingMethod == 0x00) // Coding of pixels
705       {
706         if (segmentLength < 7) break;
707         UINT topFieldLen     = (segmentData[3] << 8) + segmentData[4];
708         UINT bottomFieldLen  = (segmentData[5] << 8) + segmentData[6];
709         if (segmentLength < 7 + topFieldLen + bottomFieldLen) break;
710         object.decodeSubBlock(segmentData + 7, topFieldLen, true);
711         if (bottomFieldLen)
712           object.decodeSubBlock(segmentData + 7 + topFieldLen,
713                                 bottomFieldLen, false);
714         else
715           object.decodeSubBlock(segmentData + 7, topFieldLen, false);
716       }
717       else if (codingMethod == 0x01) // Coded as a string of characters
718         ; // TODO
719       break;
720     }
721     case 0x14: {// Display definition
722         if (segmentLength<5) break;
723         UINT ddsversion=(segmentData[0]&0xf0)>>4;
724         if (ddsversion==dds.version) break; // no update ncessary
725         dds.version=ddsversion;
726         dds.displaywindow=!(!(segmentData[0]&0x08));
727         dds.framewidth=(segmentData[1] << 8) + segmentData[2];
728         dds.frameheight=(segmentData[3] << 8) + segmentData[4];
729         if (segmentLength<13) break;
730         if (dds.displaywindow) {
731                 dds.windowx=(segmentData[4] << 8) + segmentData[5];
732                 dds.windoww=(segmentData[6] << 8) + segmentData[7]-dds.windowx;
733                 dds.windowy=(segmentData[8] << 8) + segmentData[9];
734                 dds.windowh=(segmentData[10] << 8) + segmentData[11]-dds.windowy;
735         } else {
736                 dds.windowx=0;
737                 dds.windowy=0;
738                 dds.windoww=dds.framewidth;
739                 dds.windowh=dds.frameheight;
740         }
741       break;
742     }
743     case 0x80: // End of display set
744       finishPage(page);
745       page.dirty = false;
746       page.currentDisplay = page.pendingDisplay;
747       break;
748     }
749     segmentOffset += (segmentLength + 6);
750   }
751   return false; // Fall through from while loop: we ran out of data
752 }
753
754 void DVBSubtitles::finishPage(const DVBSubtitlePage& page)
755 {
756   if (page.dirty && !osdMenuShowing) 
757   {
758     osd->clearOSD();
759   }
760
761   DVBSubtitlePage::DisplayMap::const_iterator i,j;
762   for (i = page.currentDisplay.begin(); i != page.currentDisplay.end(); ++i)
763   { // For each currently displayed region, if the region has been
764     // moved or removed, blank out its previous screen position
765     j = page.pendingDisplay.find(i->first);
766     if (j == page.pendingDisplay.end() ||
767         j->second.x != i->second.x ||
768         j->second.y != i->second.y)
769     {
770       DVBSubtitlePage::RegionMap::const_iterator region_iter;
771       region_iter = page.regions.find(i->first);
772       if (region_iter == page.regions.end()) continue;
773       Log::getInstance()->log("DVBSubs", Log::DEBUG, "Clear region %d", i->first);
774       if (!osdMenuShowing) osd->clearOSDArea(i->second.x, i->second.y,
775            region_iter->second.getWidth(), region_iter->second.getHeight(),dds);
776     }
777   }
778
779   for (i = page.pendingDisplay.begin(); i != page.pendingDisplay.end(); ++i)
780   { // Display each pending region
781     DVBSubtitlePage::RegionMap::const_iterator region_iter;
782     region_iter = page.regions.find(i->first);
783     if (region_iter == page.regions.end()) continue;
784     Log::getInstance()->log("DVBSubs", Log::DEBUG, "Display region %d", i->first);
785     if (!osdMenuShowing) osd->drawOSDBitmap(i->second.x, i->second.y,
786          region_iter->second,dds);
787   }
788 // after displaying regions set the page timeout timer
789
790   subtitleTimeoutPoint = std::chrono::system_clock::now() + std::chrono::seconds(page.timeout);
791   subtitleTimeoutPointActive = true;
792
793   Log::getInstance()->log("DVBSubs", Log::DEBUG, "SubtitleTimeout %d", page.timeout);
794 }
795
796 void DVBSubtitles::start()
797 {
798   Log::getInstance()->log("DVBSubs", Log::DEBUG, "subs start");
799
800 #ifdef DVBSDEBUG
801
802   DBG = fopen("dfifo", "a");
803
804   fprintf(DBG, "\033[H\033[2J");
805   fflush(DBG);
806
807 #endif
808
809   input_mutex.lock();
810
811   dds=DVBSubtitleDisplayDefinition();
812   running = true;
813
814   dvbsThread = std::thread([this] { threadMethod(); });
815
816   input_mutex.unlock();
817 }
818
819 void DVBSubtitles::stop()
820 {
821   Log::getInstance()->log("DVBSubs", Log::DEBUG, "subs stop");
822
823   input_mutex.lock();
824   if (!running)
825   {
826     Log::getInstance()->log("DVBSubs", Log::ERR, "STOP called, already dead!");
827     input_mutex.unlock();
828     return;
829   }
830
831   running = false; // disables put()
832   signalStop = true;
833   dvbsCond.notify_one();
834   input_mutex.unlock();
835   dvbsThread.join();
836
837   worklist.clear();
838   input_mutex.unlock();
839
840   clearDisplayedPages();
841
842
843 #ifdef DVBSDEBUG
844   fclose(DBG);
845 #endif
846 }
847
848 void DVBSubtitles::show()
849 {
850   Log::getInstance()->log("DVBSubs", Log::DEBUG, "subs show");
851
852   output_mutex.lock();
853   showing = true;
854   output_mutex.unlock();
855 }
856
857 void DVBSubtitles::hide()
858 {
859   Log::getInstance()->log("DVBSubs", Log::DEBUG, "subs hide");
860
861   showing = false;
862   clearDisplayedPages();
863 }
864
865 void DVBSubtitles::clearDisplayedPages()
866 {
867   output_mutex.lock();
868
869   PageMap::const_iterator pageEntry = pages.find(pageOnDisplay);
870   if (pageEntry != pages.end())
871   {
872     const DVBSubtitlePage& page = pageEntry->second;
873     DVBSubtitlePage::DisplayMap::const_iterator i;
874     for (i = page.currentDisplay.begin(); i != page.currentDisplay.end(); ++i)
875     {
876       DVBSubtitlePage::RegionMap::const_iterator region_iter;
877       region_iter = page.regions.find(i->first);
878       if (region_iter == page.regions.end()) continue;
879       if (!osdMenuShowing) osd->clearOSDArea(i->second.x, i->second.y,
880            region_iter->second.getWidth(), region_iter->second.getHeight(),dds);
881     }
882   }
883   pages.clear();
884   pageOnDisplay = 65536;
885
886   output_mutex.unlock();
887 }
888
889 void DVBSubtitles::pause()
890 {
891   Log::getInstance()->log("DVBSubs", Log::DEBUG, "subs pause");
892
893   input_mutex.lock();
894   if (!running)
895   {
896     Log::getInstance()->log("DVBSubs", Log::ERR, "pause called, already dead!");
897     input_mutex.unlock();
898     return;
899   }
900
901   signalPause = true;
902   dvbsCond.notify_one();
903   input_mutex.unlock();
904 }
905
906 void DVBSubtitles::unPause()
907 {
908   Log::getInstance()->log("DVBSubs", Log::DEBUG, "subs pause");
909
910   input_mutex.lock();
911   if (!running)
912   {
913     Log::getInstance()->log("DVBSubs", Log::ERR, "pause called, already dead!");
914     input_mutex.unlock();
915     return;
916   }
917
918   signalRecalcWLTO = true;
919   dvbsCond.notify_one();
920   input_mutex.unlock();
921 }
922
923 #if WIN32
924 // FIXME win pragma
925 #pragma warning(disable : 4146)
926 #endif
927
928 void DVBSubtitles::threadMethod()
929 {
930   std::unique_lock<std::mutex> ul(input_mutex);  // Lock here, force start() to return before this does anything
931
932   bool waitExpireST{};
933   bool waitExpireWL{};
934
935   std::chrono::time_point<std::chrono::system_clock> worklistTimeoutPoint;
936   bool worklistTimeoutPointActive{};
937
938   while(1)
939   {
940 #ifdef DVBSDEBUG
941
942     // TEMP DEBUG - re-enable nowPTS below when this goes
943     ULLONG nowPTS = Video::getInstance()->getCurrentTimestamp();
944     fprintf(DBG, "\033[H\033[2J");
945     fprintf(DBG, "Now: %s %llu\n", tp2str(std::chrono::system_clock::now()).c_str(), Video::getInstance()->getCurrentTimestamp());
946     for (auto p : worklist)
947     {
948       fprintf(DBG, "packet PTS %llu SIZE %i\tTIME %s\n", p.getPTS(), p.getSize(),
949         tp2str(std::chrono::system_clock::now() + std::chrono::milliseconds(PTSDifference(p.getPTS(), nowPTS) / 90)).c_str());
950     }
951     fflush(DBG);
952
953 #endif
954
955
956     if (signalStop)
957     {
958       Log::getInstance()->log("DVBSubs", Log::INFO, "Thread exiting");
959       signalStop = false;
960       return;
961     }
962     else if (signalPause)
963     {
964       signalPause = false;
965       subtitleTimeoutPointActive = false;
966       worklistTimeoutPointActive = false;
967     }
968     else if (signalRecalcWLTO) // once per incoming packet, and other times
969     {
970       signalRecalcWLTO = false;
971 #ifndef DVBSDEBUG
972       ULLONG nowPTS = Video::getInstance()->getCurrentTimestamp();
973 #endif
974
975       if (nowPTS == 0)
976       {
977         // Video is not started yet. DVBSub packet has come in. Don't set worklistTimeoutPoint,
978         // just store the packet and allow next signal to start things off
979         Log::getInstance()->log("DVBSubs", Log::DEBUG, "signalRecalcWLTO but Video PTS == 0");
980       }
981       else
982       {
983         worklistTimeoutPointActive = true;
984         Log::getInstance()->log("DVBSubs", Log::DEBUG, "Calc: Num packets available: %i", worklist.size());
985
986         ULLONG pktPTS = worklist.front().getPTS();
987         ULLONG diff = PTSDifference(pktPTS, nowPTS);
988         diff /= 90; // convert diff to ms (PTS difference is in 1/90000s)
989         if (diff < 60 * 1000)
990         {
991           worklistTimeoutPoint = std::chrono::system_clock::now() + std::chrono::milliseconds(diff);
992         }
993         else
994         {
995           // We have a problem. An action so far in the future should have
996           // been culled. Probably the action is already due and PTSDifference
997           // wrapped around. Therefore we sleep for a minimal time instead.
998
999           // FIXME check if this still works
1000           worklistTimeoutPoint = std::chrono::system_clock::now();
1001         }
1002
1003         Log::getInstance()->log("DVBSubs", Log::DEBUG, "Calc'd new worklistTimeoutPoint");
1004       }
1005     }
1006     else if (waitExpireST) // do real work - subtitletimeout
1007     {
1008       Log::getInstance()->log("DVBSubs", Log::DEBUG, "Subtitle timeout occurred");
1009       waitExpireST = false;
1010       subtitleTimeoutPointActive = false;
1011       output_mutex.lock();
1012       if (showing && !osdMenuShowing) osd->clearOSD();
1013       output_mutex.unlock();
1014     }
1015     else if (waitExpireWL) // do real work - worklist - could be multiple packets to do
1016     {
1017       Log::getInstance()->log("DVBSubs", Log::DEBUG, "Something to do");
1018
1019       waitExpireWL = false;
1020       worklistTimeoutPointActive = false;
1021
1022       ULLONG nowPTS = Video::getInstance()->getCurrentTimestamp();
1023
1024       // Guaranteed to be at least one packet in the worklist
1025       PESPacket packet = worklist.front();
1026       ULLONG pktPTS = worklist.front().getPTS();
1027
1028       while(1)
1029       {
1030         worklist.pop_front();
1031
1032         output_mutex.lock();
1033         if (showing) decodePacket(packet);
1034         output_mutex.unlock();
1035
1036         // Repeat this loop only if there is another packet and it's the same PTS as this one
1037         // Not sure this ever happens.
1038         if (worklist.size() && (worklist.front().getPTS() == pktPTS))
1039         {
1040           packet = worklist.front();
1041           continue;
1042         }
1043         else
1044         {
1045           break;
1046         }
1047       }
1048
1049       if (worklist.size())
1050       {
1051         signalRecalcWLTO = true;
1052         continue; // restart the loop and force a recalc
1053       }
1054
1055
1056         /*
1057
1058
1059         if (PTSDifference(nowPTS, pktPTS) < 2*90000     // need this? we should be here exactly on time
1060          || PTSDifference(pktPTS, nowPTS) < 4000)
1061         { // It is due for processing or discarding.
1062           worklist.pop_front();
1063
1064           output_mutex.lock();
1065           if (showing) decodePacket(packet);
1066           output_mutex.unlock();
1067         }
1068         else if (PTSDifference(pktPTS, nowPTS) >= 60*90000)
1069         { // Seems like a bad or very old entry. Get rid of it.
1070           finished = false;
1071           worklist.pop_front();
1072         }
1073 */
1074
1075
1076       // Loop has exited. Either worklist is now empty or the next packet doesn't match the PTSDifference ranges
1077     }
1078     else
1079     {
1080       // Spurious?
1081     }
1082
1083     // Either:
1084     // 1. sleep until timeout
1085     // 2. sleep until next worklist
1086     // 3. sleep until signalled
1087
1088     if (!subtitleTimeoutPointActive && !worklistTimeoutPointActive)
1089     {
1090       Log::getInstance()->log("DVBSubs", Log::DEBUG, "No WL or STT. Infinite wait");
1091       dvbsCond.wait(ul); // Wait until signalled
1092     }
1093     else if (subtitleTimeoutPointActive)
1094     {
1095       if (worklistTimeoutPointActive && (worklistTimeoutPoint < subtitleTimeoutPoint))
1096       {
1097         Log::getInstance()->log("DVBSubs", Log::DEBUG, "WaitFor: WL");
1098         if (dvbsCond.wait_until(ul, worklistTimeoutPoint) == std::cv_status::timeout) waitExpireWL = true;
1099       }
1100       else
1101       {
1102         Log::getInstance()->log("DVBSubs", Log::DEBUG, "WaitFor: ST");
1103         if (dvbsCond.wait_until(ul, subtitleTimeoutPoint) == std::cv_status::timeout) waitExpireST = true;
1104       }
1105     }
1106     else
1107     {
1108       Log::getInstance()->log("DVBSubs", Log::DEBUG, "WaitFor: WL");
1109       if (dvbsCond.wait_until(ul, worklistTimeoutPoint) == std::cv_status::timeout) waitExpireWL = true;
1110     }
1111   }
1112 /*
1113
1114   struct timespec sleeptime;
1115   sleeptime.tv_sec = 0;
1116   sleeptime.tv_nsec = 0;
1117   uint64_t wakeup = 0;
1118   timeout_clear=false;
1119   while (1)
1120   {
1121     if (subtitleTimeoutPointActive && (subtitleTimeoutPoint < std::chrono::system_clock::now())) // do we have a subtitle timeout
1122     {
1123       subtitleTimeoutPointActive = false;
1124
1125
1126 //    if (SubtitleTimeout.TimedOut())  // do we have a subtitle timeout
1127 //    {
1128       output_mutex.lock();
1129       if (showing && !osdMenuShowing) {
1130           if (!timeout_clear) {
1131                   osd->clearOSD();      // if we have the timeout, lets clear the OSD
1132                   timeout_clear=true;
1133           }
1134       }
1135       output_mutex.unlock();
1136     }
1137     else if (showing)                   // if not lets check when will we have it
1138     {
1139       // Get milliseconds until the timeout
1140       wakeup = std::chrono::duration_cast<std::chrono::milliseconds>(subtitleTimeoutPoint - std::chrono::system_clock::now()).count();
1141               Log::getInstance()->log("DVBSubs", Log::DEBUG, "A1: %lu", wakeup);
1142
1143       //                 now - begin
1144        wakeup = -(SubtitleTimeout.Elapsed());
1145
1146               Log::getInstance()->log("DVBSubs", Log::DEBUG, "A2: %lu", wakeup);
1147
1148        if (wakeup > 0 && sleeptime.tv_nsec == 0 && sleeptime.tv_sec == 0)
1149        // We are not done, we still have a Subtitle Timeout!
1150        {
1151          sleeptime.tv_sec = (int)(wakeup / 1000);
1152          sleeptime.tv_nsec = (long)(wakeup % 1000) * 10000L / 1000L * 100000L;
1153          timeout_clear=false;
1154        }
1155     }
1156
1157
1158     //if (dvbsThreadStop) return;
1159     threadCheckExit();
1160
1161
1162     threadLock();
1163     if (!threadNudged)
1164     { // We have done the current work and no more has arrived. Sleep.
1165       if (sleeptime.tv_sec == 0 && sleeptime.tv_nsec == 0)
1166       {
1167         Log::getInstance()->log("DVBSubs", Log::DEBUG, "Sleeping until nudged.");
1168         threadWaitForSignal();
1169       }
1170       else
1171       {
1172         Log::getInstance()->log("DVBSubs", Log::DEBUG, "Sleeping for %d and %d", sleeptime.tv_sec, sleeptime.tv_nsec);
1173         struct timespec targetTime;
1174
1175                 getClockRealTime(&targetTime);
1176
1177         targetTime.tv_nsec += sleeptime.tv_nsec;
1178         if (targetTime.tv_nsec > 999999999)
1179         {
1180           targetTime.tv_sec += 1;
1181           targetTime.tv_nsec -= 1000000000;
1182         }
1183         targetTime.tv_sec += sleeptime.tv_sec;
1184         threadWaitForSignalTimed(&targetTime);
1185       }
1186     }
1187     threadNudged = false;
1188     threadUnlock();
1189     bool finished = false;
1190     ULLONG nowPTS, pktPTS;
1191     int wait;
1192     while (!finished)
1193     {
1194       threadCheckExit();
1195       finished = true;
1196       pktPTS = PESPacket::PTS_INVALID;
1197       input_mutex.lock();
1198       if (!worklist.empty())
1199         pktPTS = worklist.front().getPTS();
1200       input_mutex.unlock();
1201       if (pktPTS != PESPacket::PTS_INVALID)
1202       { // An entry exists in the work list
1203         nowPTS = Video::getInstance()->getCurrentTimestamp();
1204         if (PTSDifference(nowPTS, pktPTS) < 2*90000 ||
1205             PTSDifference(pktPTS, nowPTS) < 4000)
1206         { // It is due for processing or discarding.
1207           finished = false;
1208           input_mutex.lock();
1209           if (!worklist.empty())
1210           {
1211             PESPacket packet = worklist.front();
1212             worklist.pop_front();
1213             input_mutex.unlock();
1214             output_mutex.lock();
1215             if (showing) decodePacket(packet);
1216             output_mutex.unlock();
1217           }
1218           else input_mutex.unlock();
1219         }
1220         else if (PTSDifference(pktPTS, nowPTS) >= 60*90000)
1221         { // Seems like a bad or very old entry. Get rid of it.
1222           finished = false;
1223           input_mutex.lock();
1224           if (!worklist.empty()) worklist.pop_front();
1225           input_mutex.unlock();
1226         }
1227       }
1228     }
1229     // Calculate how long to sleep for
1230     wait = SUBTITLE_TIMEOUT_MS; // Max sleep while checking subtitle timeout 
1231     sleeptime.tv_sec = 0;
1232     sleeptime.tv_nsec = 0;
1233     if (pktPTS != PESPacket::PTS_INVALID)
1234     { // A future action exists
1235       nowPTS = Video::getInstance()->getCurrentTimestamp();
1236       ULLONG diff = PTSDifference(pktPTS, nowPTS);
1237       diff /= 90; // convert diff in ms as PTS difference in 1/90000s
1238       if (diff < 60 * 1000)
1239       {
1240         if (diff < (ULLONG)wait)
1241           wait = (int)diff;
1242         sleeptime.tv_nsec = (long)(wait % 1000) * 10000L / 1000L * 100000L;
1243       }
1244       else
1245         { // We have a problem. An action so far in the future should have
1246         // been culled. Probably the action is already due and PTSDifference
1247         // wrapped around. Therefore we sleep for a minimal time instead.
1248         sleeptime.tv_nsec = 1;
1249       }
1250     }
1251   }
1252
1253   */
1254 }
1255
1256 void DVBSubtitles::setOSDMenuVisibility(bool visible)
1257 {
1258   output_mutex.lock();
1259   osdMenuShowing = visible;
1260   output_mutex.unlock();
1261 }
1262