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