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