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