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