2 Copyright 2008 Mark Calderbank
4 This file is part of VOMP.
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.
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.
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.
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>
27 /* Reference ETSI EN 300 743 V1.3.1 (2006-11) */
29 #include "dvbsubtitles.h"
31 #include "osdreceiver.h"
35 // --- cTimeMs ---------------------------------------------------------------
36 // Borrowed from the vdr tools
38 cTimeMs::cTimeMs(int Ms)
51 uint64_t cTimeMs::Now(void)
55 if (getClockRealTime(&tp) == 0)
56 return (uint64_t(tp.tv_sec)) * 1000 + tp.tv_nsec / 1000000;
58 Log::getInstance()->log("SUBTITLES", Log::ERR, "cTimeMs: clock_gettime(CLOCK_REALTIME) failed");
62 void cTimeMs::Set(int Ms)
64 isFirstCheck = true; // Timer set, the first check can be done once
67 if (Ms) initted = true;
70 bool cTimeMs::TimedOut(void)
72 if (isFirstCheck && (Now() >= begin))
74 isFirstCheck = false; // Timer timed out & the first check is done
81 uint64_t cTimeMs::Elapsed(void)
83 if (!initted) return 0;
88 //-------- End of cTimeMs borrowed from the vdr tools --------
90 DVBSubtitleCLUT::DVBSubtitleCLUT()
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]);
101 const ULONG DVBSubtitleCLUT::defaultPalette2[4] = {
102 0x00000000, 0xFFFFFFFF, 0xFF000000, 0xFF808080
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
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
179 void DVBSubtitleCLUT::setEntry(int bpp, UINT index, UCHAR Y, UCHAR Cr, UCHAR Cb, UCHAR T)
183 if (Y2 == 0) { Y2 = 16; A = 0; } else A = 255 - T;
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;
192 const Palette& DVBSubtitleCLUT::getPalette(int bpp) const
196 case 2: return palette2; break;
197 case 4: return palette4; break;
198 default: return palette8; break;
202 DVBSubtitleRegion::DVBSubtitleRegion()
208 void DVBSubtitleRegion::setDepth(UINT d)
210 if (d < 4) palette.setBpp(1 << d);
213 DVBSubtitlePage::DVBSubtitlePage()
219 serviceAcquired(false)
222 void DVBSubtitlePage::setState(UCHAR s)
225 if (state == 1) // Acquisition point - page refresh
226 if (!serviceAcquired) // Service not acquired lets treat this as mode change
228 if (state == 2) // Mode change - new epoch
230 serviceAcquired = true;
233 currentDisplay.clear();
234 pendingDisplay.clear();
239 void DVBSubtitlePage::updateRegionPalettes(const DVBSubtitleCLUT& clut)
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());
247 DVBSubtitleDisplayDefinition::DVBSubtitleDisplayDefinition()
253 class DVBSubtitleObject
257 UCHAR nonModColourFlag;
260 DVBSubtitleRegion* region;
261 UINT horizontalPosition;
262 UINT verticalPosition;
264 typedef std::vector<struct RegionRef> UseList;
269 void initMapTables();
270 UCHAR* currentMapTable;
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);
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);
285 DVBSubtitleObject::DVBSubtitleObject(UINT id)
288 currentMapTable(NULL),
294 void DVBSubtitleObject::initMapTables()
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;
304 void DVBSubtitleObject::findWhereUsed(DVBSubtitlePage& page)
307 DVBSubtitlePage::RegionMap::iterator i;
308 for (i = page.regions.begin(); i != page.regions.end(); ++i)
310 DVBSubtitleRegion::ObjectList::iterator j;
311 for (j = i->second.objects.begin(); j != i->second.objects.end(); ++j)
313 if (j->objectID == objectID)
315 struct RegionRef usage;
316 usage.region = &(i->second);
317 usage.horizontalPosition = j->horizontalPosition;
318 usage.verticalPosition = j->verticalPosition;
319 useList.push_back(usage);
325 void DVBSubtitleObject::drawLine(const RegionRef& use, UINT x, UINT y,
326 UCHAR colour, UINT len)
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);
335 UCHAR DVBSubtitleObject::get2Bits(const UCHAR* data, UINT& index)
337 UCHAR result = data[index];
344 return (result >> bitPos) & 0x03;
347 UCHAR DVBSubtitleObject::get4Bits(const UCHAR* data, UINT& index)
349 UCHAR result = data[index];
360 return result & 0x0F;
363 bool DVBSubtitleObject::decode2BppCodeString(const UCHAR* data, UINT& index, const struct RegionRef& use, UINT& x, UINT y)
365 UINT rl = 0, colour = 0;
366 UCHAR code = get2Bits(data, index);
374 code = get2Bits(data, index);
375 if (code & 2) // Switch 1
377 rl = ((code & 1) << 2) + get2Bits(data, index) + 3;
378 colour = get2Bits(data, index);
380 else if (code & 1) rl = 1; // Colour 0
383 code = get2Bits(data, index);
387 return false; // end of 2-bit/pixel_code_string
392 rl = (get2Bits(data, index) << 2) + get2Bits(data, index) + 12;
393 colour = get2Bits(data, index);
396 rl = (get2Bits(data, index) << 6) + (get2Bits(data, index) << 4) +
397 (get2Bits(data, index) << 2) + get2Bits(data, index) + 29;
398 colour = get2Bits(data, index);
403 drawLine(use, x, y, colour, rl);
408 bool DVBSubtitleObject::decode4BppCodeString(const UCHAR* data, UINT& index, const struct RegionRef& use, UINT& x, UINT y)
410 UINT rl = 0, colour = 0;
411 UCHAR code = get4Bits(data, index);
419 code = get4Bits(data, index);
420 if (code & 8) // Switch 1
422 if (code & 4) // Switch 2
424 switch (code & 3) // Switch 3
433 rl = get4Bits(data, index) + 9;
434 colour = get4Bits(data, index);
437 rl = (get4Bits(data, index) << 4) + get4Bits(data, index) + 25;
438 colour = get4Bits(data, index);
445 colour = get4Bits(data, index);
450 if (!code) return false; // end of 4-bit/pixel_code_string
451 rl = code + 2; // Colour 0
454 drawLine(use, x, y, colour, rl);
459 bool DVBSubtitleObject::decode8BppCodeString(const UCHAR* data, UINT& index, const struct RegionRef& use, UINT& x, UINT y)
461 UINT rl = 0, colour = 0;
462 UCHAR code = data[index++];
470 code = data[index++];
473 colour = data[index++];
475 return false; // else colour 0
477 drawLine(use, x, y, colour, rl);
482 void DVBSubtitleObject::decodeSubBlock(const UCHAR* data, UINT length, bool even)
484 UseList::const_iterator use;
485 for (use = useList.begin(); use != useList.end(); ++use)
487 UINT x = 0; UINT y = even ? 0 : 1; UINT index = 0;
488 while (index < length)
490 switch (data[index++])
493 switch (use->region->palette.getBpp())
495 case 8: currentMapTable = map2to8; break;
496 case 4: currentMapTable = map2to4; break;
497 default: currentMapTable = NULL;
500 while (decode2BppCodeString(data, index, *use,x,y) && index < length);
501 if (!bitPos) index++;
504 if (use->region->palette.getBpp() == 8)
505 currentMapTable = map4to8;
507 currentMapTable = NULL;
509 while (decode4BppCodeString(data, index, *use,x,y) && index < length);
510 if (!bitPos) index++;
513 while (decode8BppCodeString(data, index, *use,x,y) && index < length);
516 map2to4[0] = data[index] >> 4;
517 map2to4[1] = data[index++] & 0x0F;
518 map2to4[2] = data[index] >> 4;
519 map2to4[3] = data[index++] & 0x0F;
522 for (int i = 0; i < 4; i++) map2to8[i] = data[index++];
525 for (int i = 0; i < 16; i++) map4to8[i] = data[index++];
535 static ULLONG PTSDifference(ULLONG pts1, ULLONG pts2)
537 // Assume pts1, pts2 < 2^33; calculate pts1 - pts2
541 return (1LL<<33) + pts1 - pts2;
544 DVBSubtitles::DVBSubtitles(OSDReceiver* tosd)
546 pageOnDisplay(65536),
554 pthread_mutex_init(&input_mutex, NULL);
555 pthread_mutex_init(&output_mutex, NULL);
557 input_mutex=CreateMutex(NULL,FALSE,NULL);
558 output_mutex=CreateMutex(NULL,FALSE,NULL);
562 void DVBSubtitles::put(const PESPacket& packet)
567 if (packet.getPTS() != PESPacket::PTS_INVALID)
569 worklist.push_back(packet);
576 bool DVBSubtitles::decodePacket(const PESPacket& packet)
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)
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();
603 case 0x10: // Page composition
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();
614 while (segmentLength > parsePos + 5)
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;
628 case 0x11: // Region composition
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())
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;
658 region.objects.clear();
660 while (segmentLength > parsePos + 5)
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);
676 case 0x12: // CLUT definition
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;
689 while (segmentLength > parsePos + 3)
691 UCHAR clutEntryID = segmentData[parsePos];
692 bool fullRangeFlag = segmentData[parsePos + 1] & 0x01;
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];
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;
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;
716 page.updateRegionPalettes(clut);
719 case 0x13: // Object data
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
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);
735 object.decodeSubBlock(segmentData + 7 + topFieldLen,
736 bottomFieldLen, false);
738 object.decodeSubBlock(segmentData + 7, topFieldLen, false);
740 else if (codingMethod == 0x01) // Coded as a string of characters
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;
761 dds.windoww=dds.framewidth;
762 dds.windowh=dds.frameheight;
766 case 0x80: // End of display set
769 page.currentDisplay = page.pendingDisplay;
772 segmentOffset += (segmentLength + 6);
774 return false; // Fall through from while loop: we ran out of data
777 void DVBSubtitles::finishPage(const DVBSubtitlePage& page)
779 if (page.dirty && !osdMenuShowing)
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)
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);
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);
811 // after displaying regions set the page timeout timer
812 SubtitleTimeout.Set(page.timeout * 1000);
814 Log::getInstance()->log("SUBTITLES", Log::DEBUG, "SubtitleTimeout %d", page.timeout);
817 void DVBSubtitles::lockInput()
820 pthread_mutex_lock(&input_mutex);
822 WaitForSingleObject(input_mutex, INFINITE);
826 void DVBSubtitles::unlockInput()
829 pthread_mutex_unlock(&input_mutex);
831 ReleaseMutex(input_mutex);
835 void DVBSubtitles::lockOutput()
838 pthread_mutex_lock(&output_mutex);
840 WaitForSingleObject(output_mutex, INFINITE);
844 void DVBSubtitles::unlockOutput()
847 pthread_mutex_unlock(&output_mutex);
849 ReleaseMutex(output_mutex);
853 int DVBSubtitles::start()
856 dds=DVBSubtitleDisplayDefinition();
859 return threadStart();
862 void DVBSubtitles::stop()
870 PageMap::const_iterator pageEntry = pages.find(pageOnDisplay);
871 if (pageEntry != pages.end())
873 const DVBSubtitlePage& page = pageEntry->second;
874 DVBSubtitlePage::DisplayMap::const_iterator i;
875 for (i = page.currentDisplay.begin(); i != page.currentDisplay.end(); ++i)
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);
885 pageOnDisplay = 65536;
887 threadNudged = false;
890 void DVBSubtitles::show()
897 void DVBSubtitles::hide()
901 PageMap::const_iterator pageEntry = pages.find(pageOnDisplay);
902 if (pageEntry != pages.end())
904 const DVBSubtitlePage& page = pageEntry->second;
905 DVBSubtitlePage::DisplayMap::const_iterator i;
906 for (i = page.currentDisplay.begin(); i != page.currentDisplay.end(); ++i)
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);
916 pageOnDisplay = 65536;
920 void DVBSubtitles::nudge()
923 pthread_mutex_lock(&threadCondMutex);
925 pthread_cond_signal(&threadCond);
926 pthread_mutex_unlock(&threadCondMutex);
928 WaitForSingleObject(threadCondMutex, INFINITE);
930 SetEvent(threadCond);
931 ReleaseMutex(threadCondMutex);
935 #define SUBTITLE_TIMEOUT_MS 750
936 // How often do we check if subtitles have been timed out.
938 void DVBSubtitles::threadMethod()
940 struct timespec sleeptime;
941 sleeptime.tv_sec = 0;
942 sleeptime.tv_nsec = 0;
947 if (SubtitleTimeout.TimedOut()) // do we have a subtitle timeout
950 if (showing && !osdMenuShowing) {
951 if (!timeout_clear) {
952 osd->clearOSD(); // if we have the timeout, lets clear the OSD
958 else if (showing) // if not lets check when will we have it
960 wakeup = -(SubtitleTimeout.Elapsed());
961 if (wakeup > 0 && sleeptime.tv_nsec == 0 && sleeptime.tv_sec == 0)
962 // We are not done, we still have a Subtitle Timeout!
964 sleeptime.tv_sec = (int)(wakeup / 1000);
965 sleeptime.tv_nsec = (long)(wakeup % 1000) * 10000L / 1000L * 100000L;
972 { // We have done the current work and no more has arrived. Sleep.
973 if (sleeptime.tv_sec == 0 && sleeptime.tv_nsec == 0)
975 Log::getInstance()->log("SUBTITLES", Log::DEBUG, "Sleeping until nudged.");
976 threadWaitForSignal();
980 Log::getInstance()->log("SUBTITLES", Log::DEBUG, "Sleeping for %d and %d", sleeptime.tv_sec, sleeptime.tv_nsec);
981 struct timespec targetTime;
983 getClockRealTime(&targetTime);
985 targetTime.tv_nsec += sleeptime.tv_nsec;
986 if (targetTime.tv_nsec > 999999999)
988 targetTime.tv_sec += 1;
989 targetTime.tv_nsec -= 1000000000;
991 targetTime.tv_sec += sleeptime.tv_sec;
992 threadWaitForSignalTimed(&targetTime);
995 threadNudged = false;
997 bool finished = false;
998 ULLONG nowPTS, pktPTS;
1004 pktPTS = PESPacket::PTS_INVALID;
1006 if (!worklist.empty())
1007 pktPTS = worklist.front().getPTS();
1009 if (pktPTS != PESPacket::PTS_INVALID)
1010 { // An entry exists in the work list
1011 nowPTS = Video::getInstance()->getCurrentTimestamp();
1012 if (PTSDifference(nowPTS, pktPTS) < 2*90000 ||
1013 PTSDifference(pktPTS, nowPTS) < 4000)
1014 { // It is due for processing or discarding.
1017 if (!worklist.empty())
1019 PESPacket packet = worklist.front();
1020 worklist.pop_front();
1023 if (showing) decodePacket(packet);
1028 else if (PTSDifference(pktPTS, nowPTS) >= 60*90000)
1029 { // Seems like a bad or very old entry. Get rid of it.
1032 if (!worklist.empty()) worklist.pop_front();
1037 // Calculate how long to sleep for
1038 wait = SUBTITLE_TIMEOUT_MS; // Max sleep while checking subtitle timeout
1039 sleeptime.tv_sec = 0;
1040 sleeptime.tv_nsec = 0;
1041 if (pktPTS != PESPacket::PTS_INVALID)
1042 { // A future action exists
1043 nowPTS = Video::getInstance()->getCurrentTimestamp();
1044 ULLONG diff = PTSDifference(pktPTS, nowPTS);
1045 diff /= 90; // convert diff in ms as PTS difference in 1/90000s
1046 if (diff < 60 * 1000)
1048 if (diff < (ULLONG)wait)
1050 sleeptime.tv_nsec = (long)(wait % 1000) * 10000L / 1000L * 100000L;
1053 { // We have a problem. An action so far in the future should have
1054 // been culled. Probably the action is already due and PTSDifference
1055 // wrapped around. Therefore we sleep for a minimal time instead.
1056 sleeptime.tv_nsec = 1;
1062 void DVBSubtitles::setOSDMenuVisibility(bool visible)
1065 osdMenuShowing = visible;