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