vdr 2.6.7
dvbsubtitle.c
Go to the documentation of this file.
1/*
2 * dvbsubtitle.c: DVB subtitles
3 *
4 * See the main source file 'vdr.c' for copyright information and
5 * how to reach the author.
6 *
7 * Original author: Marco Schluessler <marco@lordzodiac.de>
8 * With some input from the "subtitles plugin" by Pekka Virtanen <pekka.virtanen@sci.fi>
9 *
10 * $Id: dvbsubtitle.c 5.2 2022/12/06 16:57:01 kls Exp $
11 */
12
13#include "dvbsubtitle.h"
14#define __STDC_FORMAT_MACROS // Required for format specifiers
15#include <inttypes.h>
16#include "device.h"
17#include "libsi/si.h"
18
19#define PAGE_COMPOSITION_SEGMENT 0x10
20#define REGION_COMPOSITION_SEGMENT 0x11
21#define CLUT_DEFINITION_SEGMENT 0x12
22#define OBJECT_DATA_SEGMENT 0x13
23#define DISPLAY_DEFINITION_SEGMENT 0x14
24#define DISPARITY_SIGNALING_SEGMENT 0x15 // DVB BlueBook A156
25#define END_OF_DISPLAY_SET_SEGMENT 0x80
26#define STUFFING_SEGMENT 0xFF
27
28#define PGS_PALETTE_SEGMENT 0x14
29#define PGS_OBJECT_SEGMENT 0x15
30#define PGS_PRESENTATION_SEGMENT 0x16
31#define PGS_WINDOW_SEGMENT 0x17
32#define PGS_DISPLAY_SEGMENT 0x80
33
34// Set these to 'true' for debug output, which is written into the file dbg-log.htm
35// in the current working directory. The HTML file shows the actual bitmaps (dbg-nnn.jpg)
36// used to display the subtitles.
37static bool DebugNormal = false; // shows pages, regions and objects
38static bool DebugVerbose = false; // shows everything
48
49#define dbgdisplay(a...) if (DebugDisplay) SD.WriteHtml(a)
50#define dbgpages(a...) if (DebugPages) SD.WriteHtml(a)
51#define dbgregions(a...) if (DebugRegions) SD.WriteHtml(a)
52#define dbgobjects(a...) if (DebugObjects) SD.WriteHtml(a)
53#define dbgconverter(a...) if (DebugConverter) SD.WriteHtml(a)
54#define dbgsegments(a...) if (DebugSegments) SD.WriteHtml(a)
55#define dbgpixel(a...) if (DebugPixel) SD.WriteHtml(a)
56#define dbgcluts(a...) if (DebugCluts) SD.WriteHtml(a)
57#define dbgoutput(a...) if (DebugOutput) SD.WriteHtml(a)
58
59#define DBGMAXBITMAPS 100 // debug output will be stopped after this many bitmaps
60#define DBGBITMAPWIDTH 400
61
62#define FIX_SUBTITLE_VERSION_BROADCASTER_STUPIDITY // some don't properly handle version numbers, which renders them useless because subtitles are not displayed
63
64// --- cSubtitleDebug --------------------------------------------------------
65
67private:
69 int imgCnt;
70 int64_t firstPts;
71 bool newFile;
72 double factor;
73public:
74 cSubtitleDebug(void) { Reset(); }
75 void Reset(void);
76 bool Active(void) { return imgCnt < DBGMAXBITMAPS; }
77 int64_t FirstPts(void) { return firstPts; }
78 void SetFirstPts(int64_t FirstPts) { if (firstPts < 0) firstPts = FirstPts; }
79 void SetFactor(double Factor) { factor = Factor; }
80 cString WriteJpeg(const cBitmap *Bitmap, int MaxX = 0, int MaxY = 0);
81 void WriteHtml(const char *Format, ...);
82 };
83
85{
86 imgCnt = 0;
87 firstPts = -1;
88 newFile = true;
89 factor = 1.0;
90}
91
92cString cSubtitleDebug::WriteJpeg(const cBitmap *Bitmap, int MaxX, int MaxY)
93{
94 if (!Active())
95 return NULL;
96 cMutexLock MutexLock(&mutex);
97 cBitmap *Scaled = Bitmap->Scaled(factor, factor, true);
98 int w = MaxX ? int(round(MaxX * factor)) : Scaled->Width();
99 int h = MaxY ? int(round(MaxY * factor)) : Scaled->Height();
100 uchar mem[w * h * 3];
101 for (int x = 0; x < w; x++) {
102 for (int y = 0; y < h; y++) {
103 tColor c = Scaled->GetColor(x, y);
104 int o = (y * w + x) * 3;
105 mem[o++] = (c & 0x00FF0000) >> 16;
106 mem[o++] = (c & 0x0000FF00) >> 8;
107 mem[o] = (c & 0x000000FF);
108 }
109 }
110 delete Scaled;
111 int Size = 0;
112 uchar *Jpeg = RgbToJpeg(mem, w, h, Size);
113 cString ImgName = cString::sprintf("dbg-%03d.jpg", imgCnt++);
114 int f = open(ImgName, O_WRONLY | O_CREAT, DEFFILEMODE);
115 if (f >= 0) {
116 if (write(f, Jpeg, Size) < 0)
117 LOG_ERROR_STR(*ImgName);
118 close(f);
119 }
120 free(Jpeg);
121 return ImgName;
122}
123
124void cSubtitleDebug::WriteHtml(const char *Format, ...)
125{
126 if (!Active())
127 return;
128 cMutexLock MutexLock(&mutex);
129 if (FILE *f = fopen("dbg-log.htm", newFile ? "w" : "a")) {
130 va_list ap;
131 va_start(ap, Format);
132 vfprintf(f, Format, ap);
133 va_end(ap);
134 fclose(f);
135 newFile = false;
136 }
137}
138
140
141// --- cSubtitleClut ---------------------------------------------------------
142
144private:
150 tColor yuv2rgb(int Y, int Cb, int Cr);
151 void SetColor(int Bpp, int Index, tColor Color);
152public:
154 void Parse(cBitStream &bs);
155 void ParsePgs(cBitStream &bs);
156 int ClutId(void) { return clutId; }
158 const cPalette *GetPalette(int Bpp);
159 };
160
162:palette2(2)
163,palette4(4)
164,palette8(8)
165{
166 int a = 0, r = 0, g = 0, b = 0;
167 clutId = ClutId;
169 // ETSI EN 300 743 10.3: 4-entry CLUT default contents
170 palette2.SetColor(0, ArgbToColor( 0, 0, 0, 0));
171 palette2.SetColor(1, ArgbToColor(255, 255, 255, 255));
172 palette2.SetColor(2, ArgbToColor(255, 0, 0, 0));
173 palette2.SetColor(3, ArgbToColor(255, 127, 127, 127));
174 // ETSI EN 300 743 10.2: 16-entry CLUT default contents
175 palette4.SetColor(0, ArgbToColor(0, 0, 0, 0));
176 for (int i = 1; i < 16; ++i) {
177 if (i < 8) {
178 r = (i & 1) ? 255 : 0;
179 g = (i & 2) ? 255 : 0;
180 b = (i & 4) ? 255 : 0;
181 }
182 else {
183 r = (i & 1) ? 127 : 0;
184 g = (i & 2) ? 127 : 0;
185 b = (i & 4) ? 127 : 0;
186 }
187 palette4.SetColor(i, ArgbToColor(255, r, g, b));
188 }
189 // ETSI EN 300 743 10.1: 256-entry CLUT default contents
190 palette8.SetColor(0, ArgbToColor(0, 0, 0, 0));
191 for (int i = 1; i < 256; ++i) {
192 if (i < 8) {
193 r = (i & 1) ? 255 : 0;
194 g = (i & 2) ? 255 : 0;
195 b = (i & 4) ? 255 : 0;
196 a = 63;
197 }
198 else {
199 switch (i & 0x88) {
200 case 0x00:
201 r = ((i & 1) ? 85 : 0) + ((i & 0x10) ? 170 : 0);
202 g = ((i & 2) ? 85 : 0) + ((i & 0x20) ? 170 : 0);
203 b = ((i & 4) ? 85 : 0) + ((i & 0x40) ? 170 : 0);
204 a = 255;
205 break;
206 case 0x08:
207 r = ((i & 1) ? 85 : 0) + ((i & 0x10) ? 170 : 0);
208 g = ((i & 2) ? 85 : 0) + ((i & 0x20) ? 170 : 0);
209 b = ((i & 4) ? 85 : 0) + ((i & 0x40) ? 170 : 0);
210 a = 127;
211 break;
212 case 0x80:
213 r = 127 + ((i & 1) ? 43 : 0) + ((i & 0x10) ? 85 : 0);
214 g = 127 + ((i & 2) ? 43 : 0) + ((i & 0x20) ? 85 : 0);
215 b = 127 + ((i & 4) ? 43 : 0) + ((i & 0x40) ? 85 : 0);
216 a = 255;
217 break;
218 case 0x88:
219 r = ((i & 1) ? 43 : 0) + ((i & 0x10) ? 85 : 0);
220 g = ((i & 2) ? 43 : 0) + ((i & 0x20) ? 85 : 0);
221 b = ((i & 4) ? 43 : 0) + ((i & 0x40) ? 85 : 0);
222 a = 255;
223 break;
224 }
225 }
226 palette8.SetColor(i, ArgbToColor(a, r, g, b));
227 }
228}
229
231{
232 int Version = bs.GetBits(4);
233#ifndef FIX_SUBTITLE_VERSION_BROADCASTER_STUPIDITY
234 if (clutVersionNumber == Version)
235 return; // no update
236#endif
237 clutVersionNumber = Version;
238 bs.SkipBits(4); // reserved
239 dbgcluts("<b>clut</b> id %d version %d<br>\n", clutId, clutVersionNumber);
240 while (!bs.IsEOF()) {
241 uchar clutEntryId = bs.GetBits(8);
242 bool entryClut2Flag = bs.GetBit();
243 bool entryClut4Flag = bs.GetBit();
244 bool entryClut8Flag = bs.GetBit();
245 bs.SkipBits(4); // reserved
246 uchar yval;
247 uchar crval;
248 uchar cbval;
249 uchar tval;
250 if (bs.GetBit()) { // full_range_flag
251 yval = bs.GetBits(8);
252 crval = bs.GetBits(8);
253 cbval = bs.GetBits(8);
254 tval = bs.GetBits(8);
255 }
256 else {
257 yval = bs.GetBits(6) << 2;
258 crval = bs.GetBits(4) << 4;
259 cbval = bs.GetBits(4) << 4;
260 tval = bs.GetBits(2) << 6;
261 }
262 tColor value = 0;
263 if (yval) {
264 value = yuv2rgb(yval, cbval, crval);
265 value |= ((10 - (clutEntryId ? Setup.SubtitleFgTransparency : Setup.SubtitleBgTransparency)) * (255 - tval) / 10) << 24;
266 }
267 dbgcluts("%2d %d %d %d %08X<br>\n", clutEntryId, entryClut2Flag ? 2 : 0, entryClut4Flag ? 4 : 0, entryClut8Flag ? 8 : 0, value);
268 if (entryClut2Flag)
269 SetColor(2, clutEntryId, value);
270 if (entryClut4Flag)
271 SetColor(4, clutEntryId, value);
272 if (entryClut8Flag)
273 SetColor(8, clutEntryId, value);
274 }
275}
276
278{
279 int Version = bs.GetBits(8);
280 if (clutVersionNumber == Version)
281 return; // no update
282 clutVersionNumber = Version;
283 dbgcluts("<b>clut</b> id %d version %d<br>\n", clutId, clutVersionNumber);
284 for (int i = 0; i < 256; ++i)
285 SetColor(8, i, ArgbToColor(0, 0, 0, 0));
286 while (!bs.IsEOF()) {
287 uchar clutEntryId = bs.GetBits(8);
288 uchar yval = bs.GetBits(8);
289 uchar crval = bs.GetBits(8);
290 uchar cbval = bs.GetBits(8);
291 uchar tval = bs.GetBits(8);
292 tColor value = 0;
293 if (yval) {
294 value = yuv2rgb(yval, cbval, crval);
295 value |= ((10 - (clutEntryId ? Setup.SubtitleFgTransparency : Setup.SubtitleBgTransparency)) * tval / 10) << 24;
296 }
297 dbgcluts("%2d %08X<br>\n", clutEntryId, value);
298 SetColor(8, clutEntryId, value);
299 }
300}
301
302tColor cSubtitleClut::yuv2rgb(int Y, int Cb, int Cr)
303{
304 int Ey, Epb, Epr;
305 int Eg, Eb, Er;
306
307 Ey = (Y - 16);
308 Epb = (Cb - 128);
309 Epr = (Cr - 128);
310 /* ITU-R 709 */
311 Er = constrain((298 * Ey + 460 * Epr) / 256, 0, 255);
312 Eg = constrain((298 * Ey - 55 * Epb - 137 * Epr) / 256, 0, 255);
313 Eb = constrain((298 * Ey + 543 * Epb ) / 256, 0, 255);
314
315 return (Er << 16) | (Eg << 8) | Eb;
316}
317
318void cSubtitleClut::SetColor(int Bpp, int Index, tColor Color)
319{
320 switch (Bpp) {
321 case 2: palette2.SetColor(Index, Color); break;
322 case 4: palette4.SetColor(Index, Color); break;
323 case 8: palette8.SetColor(Index, Color); break;
324 default: esyslog("ERROR: wrong Bpp in cSubtitleClut::SetColor(%d, %d, %08X)", Bpp, Index, Color);
325 }
326}
327
329{
330 switch (Bpp) {
331 case 2: return &palette2;
332 case 4: return &palette4;
333 case 8: return &palette8;
334 default: esyslog("ERROR: wrong Bpp in cSubtitleClut::GetPalette(%d)", Bpp);
335 }
336 return &palette8;
337}
338
339// --- cSubtitleObject -------------------------------------------------------
340
342private:
352 char *txtData;
354 void DrawLine(cBitmap *Bitmap, int x, int y, tIndex Index, int Length);
355 bool Decode2BppCodeString(cBitmap *Bitmap, int px, int py, cBitStream *bs, int&x, int y, const uint8_t *MapTable);
356 bool Decode4BppCodeString(cBitmap *Bitmap, int px, int py, cBitStream *bs, int&x, int y, const uint8_t *MapTable);
357 bool Decode8BppCodeString(cBitmap *Bitmap, int px, int py, cBitStream *bs, int&x, int y);
358 bool DecodePgsCodeString(cBitmap *Bitmap, int px, int py, cBitStream *bs, int&x, int y);
359 void DecodeSubBlock(cBitmap *Bitmap, int px, int py, const uchar *Data, int Length, bool Even);
360 void DecodeCharacterString(const uchar *Data, int NumberOfCodes);
361public:
364 void Parse(cBitStream &bs);
365 void ParsePgs(cBitStream &bs);
366 int ObjectId(void) { return objectId; }
370 void Render(cBitmap *Bitmap, int px, int py, tIndex IndexFg, tIndex IndexBg);
371 };
372
374{
378 nonModifyingColorFlag = false;
379 topLength = 0;
380 botLength = 0;
381 topIndex = 0;
382 topData = NULL;
383 botData = NULL;
384 txtData = NULL;
385 lineHeight = 26; // configurable subtitling font size?
386}
387
389{
390 free(topData);
391 free(botData);
392 free(txtData);
393}
394
396{
397 int Version = bs.GetBits(4);
398#ifndef FIX_SUBTITLE_VERSION_BROADCASTER_STUPIDITY
399 if (objectVersionNumber == Version)
400 return; // no update
401#endif
402 objectVersionNumber = Version;
405 bs.SkipBit(); // reserved
406 dbgobjects("<b>object</b> id %d version %d method %d modify %d", objectId, objectVersionNumber, objectCodingMethod, nonModifyingColorFlag); // no "<br>\n" here, DecodeCharacterString() may add data
407 if (objectCodingMethod == 0) { // coding of pixels
408 topLength = bs.GetBits(16);
409 botLength = bs.GetBits(16);
410 free(topData);
411 if ((topData = MALLOC(uchar, topLength)) != NULL)
412 memcpy(topData, bs.GetData(), topLength);
413 else
414 topLength = 0;
415 free(botData);
416 if ((botData = MALLOC(uchar, botLength)) != NULL)
417 memcpy(botData, bs.GetData() + topLength, botLength);
418 else
419 botLength = 0;
420 bs.WordAlign();
421 }
422 else if (objectCodingMethod == 1) { // coded as a string of characters
423 int numberOfCodes = bs.GetBits(8);
424 DecodeCharacterString(bs.GetData(), numberOfCodes);
425 }
426 dbgobjects("<br>\n");
427 if (DebugObjects) {
428 // We can't get the actual clut here, so we use a default one. This may lead to
429 // funny colors, but we just want to get a rough idea of what's in the object, anyway.
430 cSubtitleClut Clut(0);
431 cBitmap b(1920, 1080, 8);
432 b.Replace(*Clut.GetPalette(b.Bpp()));
433 b.Clean();
434 Render(&b, 0, 0, 0, 1);
435 int x1, y1, x2, y2;
436 if (b.Dirty(x1, y1, x2, y2)) {
437 cString ImgName = SD.WriteJpeg(&b, x2, y2);
438 dbgobjects("<img src=\"%s\"><br>\n", *ImgName);
439 }
440 }
441}
442
444{
445 int Version = bs.GetBits(8);
446 if (objectVersionNumber == Version)
447 return; // no update
448 objectVersionNumber = Version;
450 int sequenceDescriptor = bs.GetBits(8);
451 if (!(sequenceDescriptor & 0x80) && topData != NULL) {
452 memcpy(topData + topIndex, bs.GetData(), (bs.Length() - bs.Index()) / 8);
453 topIndex += (bs.Length() - bs.Index()) / 8;
454 return;
455 }
456 topLength = bs.GetBits(24) - 4 + 1; // exclude width / height, add sub block type
457 bs.SkipBits(32);
458 if ((topData = MALLOC(uchar, topLength)) != NULL) {
459 topData[topIndex++] = 0xFF; // PGS end of line
460 memcpy(topData + 1, bs.GetData(), (bs.Length() - bs.Index()) / 8);
461 topIndex += (bs.Length() - bs.Index()) / 8 + 1;
462 }
463 dbgobjects("<b>object</b> id %d version %d method %d modify %d", objectId, objectVersionNumber, objectCodingMethod, nonModifyingColorFlag);
464}
465
466void cSubtitleObject::DecodeCharacterString(const uchar *Data, int NumberOfCodes)
467{
468 // "ETSI EN 300 743 V1.3.1 (2006-11)", chapter 7.2.5 "Object data segment" specifies
469 // character_code to be a 16-bit index number into the character table identified
470 // in the subtitle_descriptor. However, the "subtitling_descriptor" <sic> according to
471 // "ETSI EN 300 468 V1.13.1 (2012-04)" doesn't contain a "character table identifier".
472 // It only contains a three letter language code, without any specification as to how
473 // this is related to a specific character table.
474 // Apparently the first "code" in textual subtitles contains the character table
475 // identifier, and all codes are 8-bit only. So let's first make Data a string of
476 // 8-bit characters:
477 if (NumberOfCodes > 0) {
478 char txt[NumberOfCodes + 1];
479 for (int i = 0; i < NumberOfCodes; i++)
480 txt[i] = Data[i * 2 + 1];
481 txt[NumberOfCodes] = 0;
482 const uchar *from = (uchar *)txt;
483 int len = NumberOfCodes;
484 const char *CharacterTable = SI::getCharacterTable(from, len);
485 dbgobjects(" table %s raw '%s'", CharacterTable, from);
486 cCharSetConv conv(CharacterTable, cCharSetConv::SystemCharacterTable());
487 const char *s = conv.Convert((const char *)from);
488 dbgobjects(" conv '%s'", s);
489 free(txtData);
490 txtData = strdup(s);
491 }
492}
493
494void cSubtitleObject::DecodeSubBlock(cBitmap *Bitmap, int px, int py, const uchar *Data, int Length, bool Even)
495{
496 int x = 0;
497 int y = Even ? 0 : 1;
498 uint8_t map2to4[ 4] = { 0x00, 0x07, 0x08, 0x0F };
499 uint8_t map2to8[ 4] = { 0x00, 0x77, 0x88, 0xFF };
500 uint8_t map4to8[16] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF };
501 const uint8_t *mapTable = NULL;
502 cBitStream bs(Data, Length * 8);
503 while (!bs.IsEOF()) {
504 switch (bs.GetBits(8)) {
505 case 0x10:
506 dbgpixel("2-bit / pixel code string<br>\n");
507 switch (Bitmap->Bpp()) {
508 case 8: mapTable = map2to8; break;
509 case 4: mapTable = map2to4; break;
510 default: mapTable = NULL; break;
511 }
512 while (Decode2BppCodeString(Bitmap, px, py, &bs, x, y, mapTable) && !bs.IsEOF())
513 ;
514 bs.ByteAlign();
515 break;
516 case 0x11:
517 dbgpixel("4-bit / pixel code string<br>\n");
518 switch (Bitmap->Bpp()) {
519 case 8: mapTable = map4to8; break;
520 default: mapTable = NULL; break;
521 }
522 while (Decode4BppCodeString(Bitmap, px, py, &bs, x, y, mapTable) && !bs.IsEOF())
523 ;
524 bs.ByteAlign();
525 break;
526 case 0x12:
527 dbgpixel("8-bit / pixel code string<br>\n");
528 while (Decode8BppCodeString(Bitmap, px, py, &bs, x, y) && !bs.IsEOF())
529 ;
530 break;
531 case 0x20:
532 dbgpixel("sub block 2 to 4 map<br>\n");
533 for (int i = 0; i < 4; ++i)
534 map2to4[i] = bs.GetBits(4);
535 break;
536 case 0x21:
537 dbgpixel("sub block 2 to 8 map<br>\n");
538 for (int i = 0; i < 4; ++i)
539 map2to8[i] = bs.GetBits(8);
540 break;
541 case 0x22:
542 dbgpixel("sub block 4 to 8 map<br>\n");
543 for (int i = 0; i < 16; ++i)
544 map4to8[i] = bs.GetBits(8);
545 break;
546 case 0xF0:
547 dbgpixel("end of object line<br>\n");
548 x = 0;
549 y += 2;
550 break;
551 case 0xFF:
552 dbgpixel("PGS code string, including EOLs<br>\n");
553 while (DecodePgsCodeString(Bitmap, px, py, &bs, x, y) && !bs.IsEOF()) {
554 x = 0;
555 y++;
556 }
557 break;
558 default: dbgpixel("unknown sub block %s %d<br>\n", __FUNCTION__, __LINE__);
559 }
560 }
561}
562
563void cSubtitleObject::DrawLine(cBitmap *Bitmap, int x, int y, tIndex Index, int Length)
564{
565 if (nonModifyingColorFlag && Index == 1)
566 return;
567 for (int pos = x; pos < x + Length; pos++)
568 Bitmap->SetIndex(pos, y, Index);
569}
570
571bool cSubtitleObject::Decode2BppCodeString(cBitmap *Bitmap, int px, int py, cBitStream *bs, int &x, int y, const uint8_t *MapTable)
572{
573 int rl = 0;
574 int color = 0;
575 uchar code = bs->GetBits(2);
576 if (code) {
577 color = code;
578 rl = 1;
579 }
580 else if (bs->GetBit()) { // switch_1
581 rl = bs->GetBits(3) + 3;
582 color = bs->GetBits(2);
583 }
584 else if (bs->GetBit()) // switch_2
585 rl = 1; //color 0
586 else {
587 switch (bs->GetBits(2)) { // switch_3
588 case 0:
589 return false;
590 case 1:
591 rl = 2; //color 0
592 break;
593 case 2:
594 rl = bs->GetBits(4) + 12;
595 color = bs->GetBits(2);
596 break;
597 case 3:
598 rl = bs->GetBits(8) + 29;
599 color = bs->GetBits(2);
600 break;
601 default: ;
602 }
603 }
604 if (MapTable)
605 color = MapTable[color];
606 DrawLine(Bitmap, px + x, py + y, color, rl);
607 x += rl;
608 return true;
609}
610
611bool cSubtitleObject::Decode4BppCodeString(cBitmap *Bitmap, int px, int py, cBitStream *bs, int &x, int y, const uint8_t *MapTable)
612{
613 int rl = 0;
614 int color = 0;
615 uchar code = bs->GetBits(4);
616 if (code) {
617 color = code;
618 rl = 1;
619 }
620 else if (bs->GetBit() == 0) { // switch_1
621 code = bs->GetBits(3);
622 if (code)
623 rl = code + 2; //color 0
624 else
625 return false;
626 }
627 else if (bs->GetBit() == 0) { // switch_2
628 rl = bs->GetBits(2) + 4;
629 color = bs->GetBits(4);
630 }
631 else {
632 switch (bs->GetBits(2)) { // switch_3
633 case 0: // color 0
634 rl = 1;
635 break;
636 case 1: // color 0
637 rl = 2;
638 break;
639 case 2:
640 rl = bs->GetBits(4) + 9;
641 color = bs->GetBits(4);
642 break;
643 case 3:
644 rl = bs->GetBits(8) + 25;
645 color = bs->GetBits(4);
646 break;
647 }
648 }
649 if (MapTable)
650 color = MapTable[color];
651 DrawLine(Bitmap, px + x, py + y, color, rl);
652 x += rl;
653 return true;
654}
655
656bool cSubtitleObject::Decode8BppCodeString(cBitmap *Bitmap, int px, int py, cBitStream *bs, int &x, int y)
657{
658 int rl = 0;
659 int color = 0;
660 uchar code = bs->GetBits(8);
661 if (code) {
662 color = code;
663 rl = 1;
664 }
665 else if (bs->GetBit()) {
666 rl = bs->GetBits(7);
667 color = bs->GetBits(8);
668 }
669 else {
670 code = bs->GetBits(7);
671 if (code)
672 rl = code; // color 0
673 else
674 return false;
675 }
676 DrawLine(Bitmap, px + x, py + y, color, rl);
677 x += rl;
678 return true;
679}
680
681bool cSubtitleObject::DecodePgsCodeString(cBitmap *Bitmap, int px, int py, cBitStream *bs, int &x, int y)
682{
683 while (!bs->IsEOF()) {
684 int color = bs->GetBits(8);
685 int rl = 1;
686 if (!color) {
687 int flags = bs->GetBits(8);
688 rl = flags & 0x3f;
689 if (flags & 0x40)
690 rl = (rl << 8) + bs->GetBits(8);
691 color = flags & 0x80 ? bs->GetBits(8) : 0;
692 }
693 if (rl > 0) {
694 DrawLine(Bitmap, px + x, py + y, color, rl);
695 x += rl;
696 }
697 else if (!rl)
698 return true;
699 }
700 return false;
701}
702
703void cSubtitleObject::Render(cBitmap *Bitmap, int px, int py, tIndex IndexFg, tIndex IndexBg)
704{
705 if (objectCodingMethod == 0) { // coding of pixels
706 DecodeSubBlock(Bitmap, px, py, topData, topLength, true);
707 if (botLength)
708 DecodeSubBlock(Bitmap, px, py, botData, botLength, false);
709 else
710 DecodeSubBlock(Bitmap, px, py, topData, topLength, false);
711 }
712 else if (objectCodingMethod == 1) { // coded as a string of characters
713 if (txtData) {
714 //TODO couldn't we draw the text directly into Bitmap?
716 cBitmap tmp(font->Width(txtData), font->Height(), Bitmap->Bpp());
717 double factor = (double)lineHeight / font->Height();
718 tmp.DrawText(0, 0, txtData, Bitmap->Color(IndexFg), Bitmap->Color(IndexBg), font);
719 cBitmap *scaled = tmp.Scaled(factor, factor, true);
720 Bitmap->DrawBitmap(px, py, *scaled);
721 delete scaled;
722 delete font;
723 }
724 }
725}
726
727// --- cSubtitleObjects ------------------------------------------------------
728
729class cSubtitleObjects : public cList<cSubtitleObject> {
730public:
731 cSubtitleObject *GetObjectById(int ObjectId, bool New = false);
732 };
733
735{
736 for (cSubtitleObject *so = First(); so; so = Next(so)) {
737 if (so->ObjectId() == ObjectId)
738 return so;
739 }
740 if (!New)
741 return NULL;
742 cSubtitleObject *Object = new cSubtitleObject(ObjectId);
743 Add(Object);
744 return Object;
745}
746
747// --- cSubtitleObjectRef ----------------------------------------------------
748
769
780
782{
783 objectId = bs.GetBits(16);
784 objectType = bs.GetBits(2);
787 bs.SkipBits(4); // reserved
789 if (objectType == 0x01 || objectType == 0x02) {
792 }
793 else {
796 }
797 dbgregions("<b>objectref</b> id %d type %d flag %d x %d y %d fg %d bg %d<br>\n", objectId, objectType, objectProviderFlag, objectHorizontalPosition, objectVerticalPosition, foregroundPixelCode, backgroundPixelCode);
798}
799
800// --- cSubtitleObjectRefPgs - PGS variant of cSubtitleObjectRef -------------
801
803private:
806 int cropX;
807 int cropY;
808 int cropW;
809 int cropH;
810public:
812};
813
816{
817 objectId = bs.GetBits(16);
818 windowId = bs.GetBits(8);
819 compositionFlag = bs.GetBits(8);
820 bs.SkipBits(32); // skip absolute position, object is aligned to region
821 if ((compositionFlag & 0x80) != 0) {
822 cropX = bs.GetBits(16);
823 cropY = bs.GetBits(16);
824 cropW = bs.GetBits(16);
825 cropH = bs.GetBits(16);
826 }
827 else
828 cropX = cropY = cropW = cropH = 0;
829 dbgregions("<b>objectrefPgs</b> id %d flag %d x %d y %d cropX %d cropY %d cropW %d cropH %d<br>\n", objectId, compositionFlag, objectHorizontalPosition, objectVerticalPosition, cropX, cropY, cropW, cropH);
830}
831
832// --- cSubtitleRegion -------------------------------------------------------
833
835private:
848public:
850 void Parse(cBitStream &bs);
851 void ParsePgs(cBitStream &bs);
852 void SetDimensions(int Width, int Height);
853 int RegionId(void) { return regionId; }
855 bool RegionFillFlag(void) { return regionFillFlag; }
856 int RegionWidth(void) { return regionWidth; }
857 int RegionHeight(void) { return regionHeight; }
859 int RegionDepth(void) { return regionDepth; }
860 int ClutId(void) { return clutId; }
861 void Render(cBitmap *Bitmap, cSubtitleObjects *Objects);
862 };
863
865{
868 regionFillFlag = false;
869 regionWidth = 0;
870 regionHeight = 0;
872 regionDepth = 0;
873 clutId = -1;
877}
878
880{
881 int Version = bs.GetBits(4);
882#ifndef FIX_SUBTITLE_VERSION_BROADCASTER_STUPIDITY
883 if (regionVersionNumber == Version)
884 return; // no update
885#endif
886 regionVersionNumber = Version;
887 regionFillFlag = bs.GetBit();
888 bs.SkipBits(3); // reserved
889 regionWidth = bs.GetBits(16);
890 regionHeight = bs.GetBits(16);
891 regionLevelOfCompatibility = 1 << bs.GetBits(3); // stored as "number of bits per pixel"
892 regionDepth = 1 << bs.GetBits(3); // stored as "number of bits per pixel"
893 bs.SkipBits(2); // reserved
894 clutId = bs.GetBits(8);
898 bs.SkipBits(2); // reserved
899 dbgregions("<b>region</b> id %d version %d fill %d width %d height %d level %d depth %d clutId %d<br>\n", regionId, regionVersionNumber, regionFillFlag, regionWidth, regionHeight, regionLevelOfCompatibility, regionDepth, clutId);
900 // no objectRefs.Clear() here!
901 while (!bs.IsEOF())
903}
904
906{
907 regionDepth = 8;
908 bs.SkipBits(8); // skip palette update flag
909 clutId = bs.GetBits(8);
910 dbgregions("<b>region</b> id %d version %d clutId %d<br>\n", regionId, regionVersionNumber, clutId);
911 int objects = bs.GetBits(8);
912 while (objects--)
914}
915
916void cSubtitleRegion::SetDimensions(int Width, int Height)
917{
918 regionWidth = Width;
919 regionHeight = Height;
920 dbgregions("<b>region</b> id %d width %d height %d<br>\n", regionId, regionWidth, regionHeight);
921}
922
924{
925 if (regionFillFlag) {
926 switch (Bitmap->Bpp()) {
927 case 2: Bitmap->Fill(region2bitPixelCode); break;
928 case 4: Bitmap->Fill(region4bitPixelCode); break;
929 case 8: Bitmap->Fill(region8bitPixelCode); break;
930 default: dbgregions("unknown bpp %d (%s %d)<br>\n", Bitmap->Bpp(), __FUNCTION__, __LINE__);
931 }
932 }
933 for (cSubtitleObjectRef *sor = objectRefs.First(); sor; sor = objectRefs.Next(sor)) {
934 if (cSubtitleObject *so = Objects->GetObjectById(sor->ObjectId())) {
935 so->Render(Bitmap, sor->ObjectHorizontalPosition(), sor->ObjectVerticalPosition(), sor->ForegroundPixelCode(), sor->BackgroundPixelCode());
936 }
937 }
938}
939
940// --- cSubtitleRegionRef ----------------------------------------------------
941
943private:
947public:
948 cSubtitleRegionRef(int id, int x, int y);
950 int RegionId(void) { return regionId; }
953 };
954
956{
957 regionId = id;
960 dbgpages("<b>regionref</b> id %d tx %d y %d<br>\n", regionId, regionHorizontalAddress, regionVerticalAddress);
961}
963{
964 regionId = bs.GetBits(8);
965 bs.SkipBits(8); // reserved
968 dbgpages("<b>regionref</b> id %d tx %d y %d<br>\n", regionId, regionHorizontalAddress, regionVerticalAddress);
969}
970
971// --- cDvbSubtitlePage ------------------------------------------------------
972
974private:
979 int64_t pts;
985public:
987 void Parse(int64_t Pts, cBitStream &bs);
988 void ParsePgs(int64_t Pts, cBitStream &bs);
989 int PageId(void) { return pageId; }
990 int PageTimeout(void) { return pageTimeout; }
992 int PageState(void) { return pageState; }
993 int64_t Pts(void) const { return pts; }
994 bool Pending(void) { return pending; }
995 cSubtitleObjects *Objects(void) { return &objects; }
996 tArea *GetAreas(int &NumAreas);
997 tArea CombineAreas(int NumAreas, const tArea *Areas);
998 tArea ScaleArea(const tArea &Area, double FactorX, double FactorY);
999 cSubtitleObject *GetObjectById(int ObjectId, bool New = false);
1000 cSubtitleClut *GetClutById(int ClutId, bool New = false);
1001 cSubtitleRegion *GetRegionById(int RegionId, bool New = false);
1002 cSubtitleRegionRef *GetRegionRefByIndex(int RegionRefIndex) { return regionRefs.Get(RegionRefIndex); }
1005 };
1006
1008{
1009 pageId = PageId;
1010 pageTimeout = 0;
1011 pageVersionNumber = -1;
1012 pageState = -1;
1013 pts = -1;
1014 pending = false;
1015}
1016
1018{
1019 if (Pts >= 0)
1020 pts = Pts;
1021 pageTimeout = bs.GetBits(8);
1022 int Version = bs.GetBits(4);
1023#ifndef FIX_SUBTITLE_VERSION_BROADCASTER_STUPIDITY
1024 if (pageVersionNumber == Version)
1025 return; // no update
1026#endif
1027 pageVersionNumber = Version;
1028 pageState = bs.GetBits(2);
1029 switch (pageState) {
1030 case 0: // normal case - page update
1031 break;
1032 case 1: // acquisition point - page refresh
1033 regions.Clear();
1034 objects.Clear();
1035 break;
1036 case 2: // mode change - new page
1037 regions.Clear();
1038 cluts.Clear();
1039 objects.Clear();
1040 break;
1041 case 3: // reserved
1042 break;
1043 default: dbgpages("unknown page state: %d<br>\n", pageState);
1044 }
1045 bs.SkipBits(2); // reserved
1046 dbgpages("<hr>\n<b>page</b> id %d version %d pts %" PRId64 " timeout %d state %d<br>\n", pageId, pageVersionNumber, pts, pageTimeout, pageState);
1047 regionRefs.Clear();
1048 while (!bs.IsEOF())
1050 pending = true;
1051}
1052
1054{
1055 if (Pts >= 0)
1056 pts = Pts;
1057 pageTimeout = 60000;
1058 int Version = bs.GetBits(16);
1059 if (pageVersionNumber == Version)
1060 return;
1061 pageVersionNumber = Version;
1062 pageState = bs.GetBits(2);
1063 switch (pageState) {
1064 case 0: // normal case - page update
1065 regions.Clear();
1066 break;
1067 case 1: // acquisition point - page refresh
1068 case 2: // epoch start - new page
1069 case 3: // epoch continue - new page
1070 regions.Clear();
1071 cluts.Clear();
1072 objects.Clear();
1073 break;
1074 default: dbgpages("unknown page state: %d<br>\n", pageState);
1075 }
1076 bs.SkipBits(6);
1077 dbgpages("<hr>\n<b>page</b> id %d version %d pts %" PRId64 " timeout %d state %d<br>\n", pageId, pageVersionNumber, pts, pageTimeout, pageState);
1078 regionRefs.Clear();
1079 pending = true;
1080}
1081
1083{
1084 if (regions.Count() > 0) {
1085 NumAreas = regionRefs.Count();
1086 tArea *Areas = new tArea[NumAreas];
1087 tArea *a = Areas;
1088 for (cSubtitleRegionRef *srr = regionRefs.First(); srr; srr = regionRefs.Next(srr)) {
1089 if (cSubtitleRegion *sr = GetRegionById(srr->RegionId())) {
1090 a->x1 = srr->RegionHorizontalAddress();
1091 a->y1 = srr->RegionVerticalAddress();
1092 a->x2 = srr->RegionHorizontalAddress() + sr->RegionWidth() - 1;
1093 a->y2 = srr->RegionVerticalAddress() + sr->RegionHeight() - 1;
1094 a->bpp = sr->RegionDepth();
1095 }
1096 else
1097 a->x1 = a->y1 = a->x2 = a->y2 = a->bpp = 0;
1098 a++;
1099 }
1100 return Areas;
1101 }
1102 NumAreas = 0;
1103 return NULL;
1104}
1105
1106tArea cDvbSubtitlePage::CombineAreas(int NumAreas, const tArea *Areas)
1107{
1108 tArea a;
1109 a.x1 = INT_MAX;
1110 a.x2 = INT_MIN;
1111 a.y1 = INT_MAX;
1112 a.y2 = INT_MIN;
1113 a.bpp = 1;
1114 for (int i = 0; i < NumAreas; i++) {
1115 a.x1 = min(a.x1, Areas[i].x1);
1116 a.x2 = max(a.x2, Areas[i].x2);
1117 a.y1 = min(a.y1, Areas[i].y1);
1118 a.y2 = max(a.y2, Areas[i].y2);
1119 a.bpp = max(a.bpp, Areas[i].bpp);
1120 }
1121 return a;
1122}
1123
1124tArea cDvbSubtitlePage::ScaleArea(const tArea &Area, double FactorX, double FactorY)
1125{
1126 tArea a;
1127 a.x1 = int(round(FactorX * Area.x1) );
1128 a.x2 = int(round(FactorX * Area.x2) - 1);
1129 a.y1 = int(round(FactorY * Area.y1) );
1130 a.y2 = int(round(FactorY * Area.y2) - 1);
1131 a.bpp = Area.bpp;
1132 while ((a.Width() & 3) != 0)
1133 a.x2++; // aligns width to a multiple of 4, so 2, 4 and 8 bpp will work
1134 return a;
1135}
1136
1138{
1139 for (cSubtitleClut *sc = cluts.First(); sc; sc = cluts.Next(sc)) {
1140 if (sc->ClutId() == ClutId)
1141 return sc;
1142 }
1143 if (!New)
1144 return NULL;
1145 cSubtitleClut *Clut = new cSubtitleClut(ClutId);
1146 cluts.Add(Clut);
1147 return Clut;
1148}
1149
1151{
1152 for (cSubtitleRegion *sr = regions.First(); sr; sr = regions.Next(sr)) {
1153 if (sr->RegionId() == RegionId)
1154 return sr;
1155 }
1156 if (!New)
1157 return NULL;
1158 cSubtitleRegion *Region = new cSubtitleRegion(RegionId);
1159 regions.Add(Region);
1160 return Region;
1161}
1162
1164{
1165 return objects.GetObjectById(ObjectId, New);
1166}
1167
1168// --- cDvbSubtitleAssembler -------------------------------------------------
1169
1171private:
1174 int pos;
1175 int size;
1176 bool Realloc(int Size);
1177public:
1179 virtual ~cDvbSubtitleAssembler();
1180 void Reset(void);
1181 unsigned char *Get(int &Length);
1182 void Put(const uchar *Data, int Length);
1183 };
1184
1186{
1187 data = NULL;
1188 size = 0;
1189 Reset();
1190}
1191
1196
1198{
1199 length = 0;
1200 pos = 0;
1201}
1202
1204{
1205 if (Size > size) {
1206 Size = max(Size, 2048);
1207 if (uchar *NewBuffer = (uchar *)realloc(data, Size)) {
1208 size = Size;
1209 data = NewBuffer;
1210 }
1211 else {
1212 esyslog("ERROR: can't allocate memory for subtitle assembler");
1213 length = 0;
1214 size = 0;
1215 free(data);
1216 data = NULL;
1217 return false;
1218 }
1219 }
1220 return true;
1221}
1222
1223unsigned char *cDvbSubtitleAssembler::Get(int &Length)
1224{
1225 if (length > pos + 5) {
1226 Length = (data[pos + 4] << 8) + data[pos + 5] + 6;
1227 if (length >= pos + Length) {
1228 unsigned char *result = data + pos;
1229 pos += Length;
1230 return result;
1231 }
1232 }
1233 return NULL;
1234}
1235
1236void cDvbSubtitleAssembler::Put(const uchar *Data, int Length)
1237{
1238 if (Length && Realloc(length + Length)) {
1239 memcpy(data + length, Data, Length);
1240 length += Length;
1241 }
1242}
1243
1244// --- cDvbSubtitleBitmaps ---------------------------------------------------
1245
1247private:
1249 int64_t pts;
1258public:
1259 cDvbSubtitleBitmaps(int State, int64_t Pts, int Timeout, tArea *Areas, int NumAreas, double OsdFactorX, double OsdFactorY, tArea &AreaCombined, tArea &AreaOsd);
1261 int State(void) { return state; }
1262 int64_t Pts(void) { return pts; }
1263 int Timeout(void) { return timeout; }
1264 void AddBitmap(cBitmap *Bitmap);
1265 bool HasBitmaps(void) { return bitmaps.Size(); }
1266 void Draw(cOsd *Osd);
1267 void DbgDump(int WindowWidth, int WindowHeight);
1268 };
1269
1270cDvbSubtitleBitmaps::cDvbSubtitleBitmaps(int State, int64_t Pts, int Timeout, tArea *Areas, int NumAreas, double OsdFactorX, double OsdFactorY, tArea &AreaCombined, tArea &AreaOsd)
1271{
1272 state = State;
1273 pts = Pts;
1274 timeout = Timeout;
1275 areas = Areas;
1276 numAreas = NumAreas;
1277 areaCombined = AreaCombined;
1278 areaOsd = AreaOsd;
1279 osdFactorX = OsdFactorX;
1280 osdFactorY = OsdFactorY;
1281}
1282
1284{
1285 delete[] areas;
1286 for (int i = 0; i < bitmaps.Size(); i++)
1287 delete bitmaps[i];
1288}
1289
1291{
1292 bitmaps.Append(Bitmap);
1293}
1294
1296{
1297 bool Scale = !(DoubleEqual(osdFactorX, 1.0) && DoubleEqual(osdFactorY, 1.0));
1298 bool AntiAlias = Setup.AntiAlias;
1299 if (Scale && osdFactorX > 1.0 || osdFactorY > 1.0) {
1300 // Upscaling requires 8bpp:
1301 int Bpp = areaOsd.bpp;
1302 areaOsd.bpp = 8;
1303 if (Osd->CanHandleAreas(&areaOsd, 1) != oeOk) {
1304 areaOsd.bpp = Bpp;
1305 AntiAlias = false;
1306 }
1307 }
1308 if (State() == 0 || Osd->SetAreas(&areaOsd, 1) == oeOk) {
1311 for (int i = 0; i < bitmaps.Size(); i++) {
1312 // merge bitmaps into combined
1313 cBitmap *b = bitmaps[i];
1314 combined.DrawBitmap(b->X0(), b->Y0(), *b);
1315 }
1316 Osd->DrawScaledBitmap(int(round(combined.X0() * osdFactorX)), int(round(combined.Y0() * osdFactorY)), combined, osdFactorX, osdFactorY, AntiAlias);
1317 Osd->Flush();
1318 }
1319}
1320
1321void cDvbSubtitleBitmaps::DbgDump(int WindowWidth, int WindowHeight)
1322{
1323 if (!SD.Active())
1324 return;
1325 SD.SetFirstPts(Pts());
1326 double STC = double(cDevice::PrimaryDevice()->GetSTC() - SD.FirstPts()) / 90000;
1327 double Start = double(Pts() - SD.FirstPts()) / 90000;
1328 double Duration = Timeout();
1329 double End = Start + Duration;
1330 cBitmap Bitmap(WindowWidth, WindowHeight, 8);
1331#define DBGBACKGROUND 0xA0A0A0
1332 Bitmap.DrawRectangle(0, 0, WindowWidth - 1, WindowHeight - 1, DBGBACKGROUND);
1333 for (int i = 0; i < bitmaps.Size(); i++) {
1334 cBitmap *b = bitmaps[i];
1335 Bitmap.DrawBitmap(b->X0(), b->Y0(), *b);
1336 }
1337 cString ImgName = SD.WriteJpeg(&Bitmap);
1338#define BORDER //" border=1"
1339 SD.WriteHtml("<p>%s<br>", State() == 0 ? "page update" : State() == 1 ? "page refresh" : State() == 2 ? "new page" : "???");
1340 SD.WriteHtml("<table" BORDER "><tr><td>");
1341 SD.WriteHtml("%.2f", STC);
1342 SD.WriteHtml("</td><td>");
1343 SD.WriteHtml("<img src=\"%s\">", *ImgName);
1344 SD.WriteHtml("</td><td style=\"height:100%%\"><table" BORDER " style=\"height:100%%\">");
1345 SD.WriteHtml("<tr><td valign=top><b>%.2f</b></td></tr>", Start);
1346 SD.WriteHtml("<tr><td valign=middle>%.2f</td></tr>", Duration);
1347 SD.WriteHtml("<tr><td valign=bottom>%.2f</td></tr>", End);
1348 SD.WriteHtml("</table></td>");
1349 SD.WriteHtml("</tr></table>\n");
1350}
1351
1352// --- cDvbSubtitleConverter -------------------------------------------------
1353
1355
1357:cThread("subtitle converter")
1358{
1360 osd = NULL;
1361 frozen = false;
1362 ddsVersionNumber = -1;
1363 displayWidth = windowWidth = 720;
1369 SD.Reset();
1370 Start();
1371}
1372
1374{
1375 Cancel(3);
1376 delete dvbSubtitleAssembler;
1377 delete osd;
1378 delete bitmaps;
1379 delete pages;
1380}
1381
1386
1388{
1389 dbgconverter("converter reset -----------------------<br>\n");
1391 Lock();
1392 pages->Clear();
1393 bitmaps->Clear();
1394 DELETENULL(osd);
1395 frozen = false;
1396 ddsVersionNumber = -1;
1397 displayWidth = windowWidth = 720;
1401 Unlock();
1402}
1403
1405{
1406 if (Data && Length > 8) {
1407 int PayloadOffset = PesPayloadOffset(Data);
1408 int SubstreamHeaderLength = 4;
1409 bool ResetSubtitleAssembler = Data[PayloadOffset + 3] == 0x00;
1410
1411 // Compatibility mode for old subtitles plugin:
1412 if ((Data[7] & 0x01) && (Data[PayloadOffset - 3] & 0x81) == 0x01 && Data[PayloadOffset - 2] == 0x81) {
1413 PayloadOffset--;
1414 SubstreamHeaderLength = 1;
1415 ResetSubtitleAssembler = Data[8] >= 5;
1416 }
1417
1418 if (Length > PayloadOffset + SubstreamHeaderLength) {
1419 int64_t pts = PesHasPts(Data) ? PesGetPts(Data) : -1;
1420 if (pts >= 0)
1421 dbgconverter("converter PTS: %" PRId64 "<br>\n", pts);
1422 const uchar *data = Data + PayloadOffset + SubstreamHeaderLength; // skip substream header
1423 int length = Length - PayloadOffset - SubstreamHeaderLength; // skip substream header
1424 if (ResetSubtitleAssembler)
1426
1427 if (length > 3) {
1428 if (data[0] == 0x20 && data[1] == 0x00 && data[2] == 0x0F)
1429 dvbSubtitleAssembler->Put(data + 2, length - 2);
1430 else
1431 dvbSubtitleAssembler->Put(data, length);
1432
1433 int Count;
1434 while (true) {
1435 unsigned char *b = dvbSubtitleAssembler->Get(Count);
1436 if (b && b[0] == 0x0F) {
1437 if (ExtractSegment(b, Count, pts) == -1)
1438 break;
1439 }
1440 else
1441 break;
1442 }
1443 }
1444 }
1445 return Length;
1446 }
1447 return 0;
1448}
1449
1450int cDvbSubtitleConverter::Convert(const uchar *Data, int Length)
1451{
1452 if (Data && Length > 8) {
1453 int PayloadOffset = PesPayloadOffset(Data);
1454 if (Length > PayloadOffset) {
1455 int64_t pts = PesHasPts(Data) ? PesGetPts(Data) : -1;
1456 if (pts >= 0)
1457 dbgconverter("converter PTS: %" PRId64 "<br>\n", pts);
1458 const uchar *data = Data + PayloadOffset;
1459 int length = Length - PayloadOffset;
1460 if (length > 0) {
1461 if (length > 2 && data[0] == 0x20 && data[1] == 0x00 && data[2] == 0x0F) {
1462 data += 2;
1463 length -= 2;
1464 }
1465 const uchar *b = data;
1466 while (length > 0) {
1467 if (b[0] == STUFFING_SEGMENT)
1468 break;
1469 int n;
1470 if (b[0] == 0x0F)
1471 n = ExtractSegment(b, length, pts);
1472 else
1473 n = ExtractPgsSegment(b, length, pts);
1474 if (n < 0)
1475 break;
1476 b += n;
1477 length -= n;
1478 }
1479 }
1480 }
1481 return Length;
1482 }
1483 return 0;
1484}
1485
1486#define LimitTo32Bit(n) ((n) & 0x00000000FFFFFFFFL)
1487
1489{
1490 int LastSetupLevel = setupLevel;
1491 cTimeMs Timeout;
1492 while (Running()) {
1493 int WaitMs = 100;
1494 if (!frozen) {
1496 if (osd) {
1497 int NewSetupLevel = setupLevel;
1498 if (Timeout.TimedOut() || LastSetupLevel != NewSetupLevel) {
1499 dbgoutput("closing osd<br>\n");
1500 DELETENULL(osd);
1501 }
1502 LastSetupLevel = NewSetupLevel;
1503 }
1504 for (cDvbSubtitleBitmaps *sb = bitmaps->First(); sb; sb = bitmaps->Next(sb)) {
1505 // Calculate the Delta between the STC (the current timestamp of the video)
1506 // and the bitmap's PTS (the timestamp when the bitmap shall be presented).
1507 // A negative Delta means that the bitmap will be presented in the future:
1508 int64_t STC = cDevice::PrimaryDevice()->GetSTC();
1509 int64_t Delta = LimitTo32Bit(STC) - LimitTo32Bit(sb->Pts()); // some devices only deliver 32 bits
1510 if (Delta > (int64_t(1) << 31))
1511 Delta -= (int64_t(1) << 32);
1512 else if (Delta < -((int64_t(1) << 31) - 1))
1513 Delta += (int64_t(1) << 32);
1514 Delta /= 90; // STC and PTS are in 1/90000s
1515 if (Delta >= 0) { // found a bitmap that shall be displayed...
1516 if (Delta < sb->Timeout() * 1000) { // ...and has not timed out yet
1517 if (!sb->HasBitmaps()) {
1518 Timeout.Set();
1519 WaitMs = 0;
1520 }
1521 else if (AssertOsd()) {
1522 dbgoutput("showing bitmap #%d of %d<br>\n", sb->Index() + 1, bitmaps->Count());
1523 sb->Draw(osd);
1524 Timeout.Set(sb->Timeout() * 1000);
1525 dbgconverter("PTS: %" PRId64 " STC: %" PRId64 " (%" PRId64 ") timeout: %d<br>\n", sb->Pts(), STC, Delta, sb->Timeout());
1526 }
1527 }
1528 else
1529 WaitMs = 0; // bitmap already timed out, so try next one immediately
1530 dbgoutput("deleting bitmap #%d of %d<br>\n", sb->Index() + 1, bitmaps->Count());
1531 bitmaps->Del(sb);
1532 break;
1533 }
1534 }
1535 }
1536 cCondWait::SleepMs(WaitMs);
1537 }
1538}
1539
1541{
1542 int OsdWidth, OsdHeight;
1543 double OsdAspect;
1544 int VideoWidth, VideoHeight;
1545 double VideoAspect;
1546 cDevice::PrimaryDevice()->GetOsdSize(OsdWidth, OsdHeight, OsdAspect);
1547 cDevice::PrimaryDevice()->GetVideoSize(VideoWidth, VideoHeight, VideoAspect);
1548 if (OsdWidth == displayWidth && OsdHeight == displayHeight) {
1549 osdFactorX = osdFactorY = 1.0;
1550 osdDeltaX = osdDeltaY = 0;
1551 }
1552 else {
1553 osdFactorX = osdFactorY = min(double(OsdWidth) / displayWidth, double(OsdHeight) / displayHeight);
1554 osdDeltaX = (OsdWidth - displayWidth * osdFactorX) / 2;
1555 osdDeltaY = (OsdHeight - displayHeight * osdFactorY) / 2;
1556 }
1557}
1558
1560{
1562 if (!osd) {
1563 SetOsdData();
1565 }
1566 return osd != NULL;
1567}
1568
1570{
1571 for (cDvbSubtitlePage *sp = pages->First(); sp; sp = pages->Next(sp)) {
1572 if (sp->PageId() == PageId)
1573 return sp;
1574 }
1575 if (!New)
1576 return NULL;
1577 cDvbSubtitlePage *Page = new cDvbSubtitlePage(PageId);
1578 pages->Add(Page);
1579 return Page;
1580}
1581
1582int cDvbSubtitleConverter::ExtractSegment(const uchar *Data, int Length, int64_t Pts)
1583{
1584 cBitStream bs(Data, Length * 8);
1585 if (Length > 5 && bs.GetBits(8) == 0x0F) { // sync byte
1586 int segmentType = bs.GetBits(8);
1587 if (segmentType == STUFFING_SEGMENT)
1588 return -1;
1590 cDvbSubtitlePage *page = GetPageById(bs.GetBits(16), true);
1591 int segmentLength = bs.GetBits(16);
1592 if (!bs.SetLength(bs.Index() + segmentLength * 8))
1593 return -1;
1594 switch (segmentType) {
1596 if (page->Pending()) {
1597 dbgsegments("END_OF_DISPLAY_SET_SEGMENT (simulated)<br>\n");
1598 FinishPage(page);
1599 }
1600 dbgsegments("PAGE_COMPOSITION_SEGMENT<br>\n");
1601 page->Parse(Pts, bs);
1603 break;
1604 }
1606 dbgsegments("REGION_COMPOSITION_SEGMENT<br>\n");
1607 cSubtitleRegion *region = page->GetRegionById(bs.GetBits(8), true);
1608 region->Parse(bs);
1609 break;
1610 }
1612 dbgsegments("CLUT_DEFINITION_SEGMENT<br>\n");
1613 cSubtitleClut *clut = page->GetClutById(bs.GetBits(8), true);
1614 clut->Parse(bs);
1615 break;
1616 }
1617 case OBJECT_DATA_SEGMENT: {
1618 dbgsegments("OBJECT_DATA_SEGMENT<br>\n");
1619 cSubtitleObject *object = page->GetObjectById(bs.GetBits(16), true);
1620 object->Parse(bs);
1621 break;
1622 }
1624 dbgsegments("DISPLAY_DEFINITION_SEGMENT<br>\n");
1625 int version = bs.GetBits(4);
1626#ifndef FIX_SUBTITLE_VERSION_BROADCASTER_STUPIDITY
1627 if (version == ddsVersionNumber)
1628 break; // no update
1629#endif
1630 bool displayWindowFlag = bs.GetBit();
1633 bs.SkipBits(3); // reserved
1634 displayWidth = windowWidth = bs.GetBits(16) + 1;
1635 displayHeight = windowHeight = bs.GetBits(16) + 1;
1636 if (displayWindowFlag) {
1637 windowHorizontalOffset = bs.GetBits(16); // displayWindowHorizontalPositionMinimum
1638 windowWidth = bs.GetBits(16) - windowHorizontalOffset + 1; // displayWindowHorizontalPositionMaximum
1639 windowVerticalOffset = bs.GetBits(16); // displayWindowVerticalPositionMinimum
1640 windowHeight = bs.GetBits(16) - windowVerticalOffset + 1; // displayWindowVerticalPositionMaximum
1641 }
1642 SetOsdData();
1643 ddsVersionNumber = version;
1644 dbgdisplay("<b>display</b> version %d flag %d width %d height %d ofshor %d ofsver %d<br>\n", ddsVersionNumber, displayWindowFlag, windowWidth, windowHeight, windowHorizontalOffset, windowVerticalOffset);
1645 break;
1646 }
1648 dbgsegments("DISPARITY_SIGNALING_SEGMENT<br>\n");
1649 bs.SkipBits(4); // dss_version_number
1650 bool disparity_shift_update_sequence_page_flag = bs.GetBit();
1651 bs.SkipBits(3); // reserved
1652 bs.SkipBits(8); // page_default_disparity_shift
1653 if (disparity_shift_update_sequence_page_flag) {
1654 bs.SkipBits(8); // disparity_shift_update_sequence_length
1655 bs.SkipBits(24); // interval_duration[23..0]
1656 int division_period_count = bs.GetBits(8);
1657 for (int i = 0; i < division_period_count; ++i) {
1658 bs.SkipBits(8); // interval_count
1659 bs.SkipBits(8); // disparity_shift_update_integer_part
1660 }
1661 }
1662 while (!bs.IsEOF()) {
1663 bs.SkipBits(8); // region_id
1664 bool disparity_shift_update_sequence_region_flag = bs.GetBit();
1665 bs.SkipBits(5); // reserved
1666 int number_of_subregions_minus_1 = bs.GetBits(2);
1667 for (int i = 0; i <= number_of_subregions_minus_1; ++i) {
1668 if (number_of_subregions_minus_1 > 0) {
1669 bs.SkipBits(16); // subregion_horizontal_position
1670 bs.SkipBits(16); // subregion_width
1671 }
1672 bs.SkipBits(8); // subregion_disparity_shift_integer_part
1673 bs.SkipBits(4); // subregion_disparity_shift_fractional_part
1674 bs.SkipBits(4); // reserved
1675 if (disparity_shift_update_sequence_region_flag) {
1676 bs.SkipBits(8); // disparity_shift_update_sequence_length
1677 bs.SkipBits(24); // interval_duration[23..0]
1678 int division_period_count = bs.GetBits(8);
1679 for (int i = 0; i < division_period_count; ++i) {
1680 bs.SkipBits(8); // interval_count
1681 bs.SkipBits(8); // disparity_shift_update_integer_part
1682 }
1683 }
1684 }
1685 }
1686 break;
1687 }
1689 dbgsegments("END_OF_DISPLAY_SET_SEGMENT<br>\n");
1690 FinishPage(page);
1691 page->SetPending(false);
1692 break;
1693 }
1694 default:
1695 dbgsegments("*** unknown segment type: %02X<br>\n", segmentType);
1696 }
1697 return bs.Length() / 8;
1698 }
1699 return -1;
1700}
1701
1702int cDvbSubtitleConverter::ExtractPgsSegment(const uchar *Data, int Length, int64_t Pts)
1703{
1704 cBitStream bs(Data, Length * 8);
1705 if (Length >= 3) {
1706 int segmentType = bs.GetBits(8);
1707 int segmentLength = bs.GetBits(16);
1708 if (!bs.SetLength(bs.Index() + segmentLength * 8))
1709 return -1;
1711 cDvbSubtitlePage *page = GetPageById(0, true);
1712 switch (segmentType) {
1714 if (page->Pending()) {
1715 dbgsegments("PGS_DISPLAY_SEGMENT (simulated)<br>\n");
1716 FinishPage(page);
1717 }
1718 dbgsegments("PGS_PRESENTATION_SEGMENT<br>\n");
1719 displayWidth = windowWidth = bs.GetBits(16);
1721 bs.SkipBits(8);
1722 page->ParsePgs(Pts, bs);
1724 cSubtitleRegion *region = page->GetRegionById(0, true);
1725 region->ParsePgs(bs);
1726 break;
1727 }
1728 case PGS_WINDOW_SEGMENT: {
1729 bs.SkipBits(16);
1730 int regionHorizontalAddress = bs.GetBits(16);
1731 int regionVerticalAddress = bs.GetBits(16);
1732 int regionWidth = bs.GetBits(16);
1733 int regionHeight = bs.GetBits(16);
1734 cSubtitleRegion *region = page->GetRegionById(0, true);
1735 region->SetDimensions(regionWidth, regionHeight);
1736 page->AddRegionRef(new cSubtitleRegionRef(0, regionHorizontalAddress, regionVerticalAddress));
1737 dbgsegments("PGS_WINDOW_SEGMENT<br>\n");
1738 break;
1739 }
1740 case PGS_PALETTE_SEGMENT: {
1741 dbgsegments("PGS_PALETTE_SEGMENT<br>\n");
1742 cSubtitleClut *clut = page->GetClutById(bs.GetBits(8), true);
1743 clut->ParsePgs(bs);
1744 break;
1745 }
1746 case PGS_OBJECT_SEGMENT: {
1747 dbgsegments("PGS_OBJECT_SEGMENT<br>\n");
1748 cSubtitleObject *object = page->GetObjectById(bs.GetBits(16), true);
1749 object->ParsePgs(bs);
1750 break;
1751 }
1752 case PGS_DISPLAY_SEGMENT: {
1753 dbgsegments("PGS_DISPLAY_SEGMENT<br>\n");
1754 FinishPage(page);
1755 page->SetPending(false);
1756 break;
1757 }
1758 default:
1759 dbgsegments("*** unknown segment type: %02X<br>\n", segmentType);
1760 return -1;
1761 }
1762 return bs.Length() / 8;
1763 }
1764 return -1;
1765}
1766
1768{
1769 if (!AssertOsd())
1770 return;
1771 int NumAreas;
1772 tArea *Areas = Page->GetAreas(NumAreas);
1773 if (!Areas)
1774 return;
1775 tArea AreaCombined = Page->CombineAreas(NumAreas, Areas);
1776 tArea AreaOsd = Page->ScaleArea(AreaCombined, osdFactorX, osdFactorY);
1777 int Bpp = 8;
1778 bool Reduced = false;
1779 if (osd) {
1780 while (osd->CanHandleAreas(&AreaOsd, 1) != oeOk) {
1781 dbgoutput("CanHandleAreas: %d<br>\n", osd->CanHandleAreas(&AreaOsd, 1));
1782 int HalfBpp = Bpp / 2;
1783 if (HalfBpp >= 2) {
1784 if (AreaOsd.bpp >= Bpp) {
1785 AreaOsd.bpp = HalfBpp;
1786 Reduced = true;
1787 }
1788 Bpp = HalfBpp;
1789 }
1790 else
1791 return; // unable to draw bitmaps
1792 }
1793 }
1794 cDvbSubtitleBitmaps *Bitmaps = new cDvbSubtitleBitmaps(Page->PageState(), Page->Pts(), Page->PageTimeout(), Areas, NumAreas, osdFactorX, osdFactorY, AreaCombined, AreaOsd);
1795 bitmaps->Add(Bitmaps);
1796 for (int i = 0; i < NumAreas; i++) {
1797 if (cSubtitleRegionRef *srr = Page->GetRegionRefByIndex(i)) {
1798 if (cSubtitleRegion *sr = Page->GetRegionById(srr->RegionId())) {
1799 if (cSubtitleClut *clut = Page->GetClutById(sr->ClutId())) {
1800 cBitmap *bm = new cBitmap(sr->RegionWidth(), sr->RegionHeight(), sr->RegionDepth());
1801 bm->Replace(*clut->GetPalette(sr->RegionDepth()));
1802 sr->Render(bm, Page->Objects());
1803 if (Reduced) {
1804 if (sr->RegionDepth() != Areas[i].bpp) {
1805 if (sr->RegionLevelOfCompatibility() <= Areas[i].bpp) {
1806 //TODO this is untested - didn't have any such subtitle stream
1807 cSubtitleClut *Clut = Page->GetClutById(sr->ClutId());
1808 dbgregions("reduce region %d bpp %d level %d area bpp %d<br>\n", sr->RegionId(), sr->RegionDepth(), sr->RegionLevelOfCompatibility(), Areas[i].bpp);
1809 bm->ReduceBpp(*Clut->GetPalette(sr->RegionDepth()));
1810 }
1811 else {
1812 dbgregions("condense region %d bpp %d level %d area bpp %d<br>\n", sr->RegionId(), sr->RegionDepth(), sr->RegionLevelOfCompatibility(), Areas[i].bpp);
1813 bm->ShrinkBpp(Areas[i].bpp);
1814 }
1815 }
1816 }
1817 bm->SetOffset(srr->RegionHorizontalAddress(), srr->RegionVerticalAddress());
1818 Bitmaps->AddBitmap(bm);
1819 }
1820 }
1821 }
1822 }
1823 if (DebugPages)
1824 Bitmaps->DbgDump(windowWidth, windowHeight);
1825}
void SkipBit(void)
Definition tools.h:396
int Index(void) const
Definition tools.h:400
void WordAlign(void)
Definition tools.c:1479
bool SetLength(int Length)
Definition tools.c:1486
int Length(void) const
Definition tools.h:399
bool IsEOF(void) const
Definition tools.h:397
const uint8_t * GetData(void) const
Definition tools.h:401
void SkipBits(int n)
Definition tools.h:395
uint32_t GetBits(int n)
Definition tools.c:1464
void ByteAlign(void)
Definition tools.c:1472
int GetBit(void)
Definition tools.c:1455
Definition osd.h:169
void ShrinkBpp(int NewBpp)
Shrinks the color depth of the bitmap to NewBpp by keeping only the 2^NewBpp most frequently used col...
Definition osd.c:796
void SetOffset(int X0, int Y0)
Sets the offset of this bitmap to the given values.
Definition osd.h:195
void ReduceBpp(const cPalette &Palette)
Reduces the color depth of the bitmap to that of the given Palette.
Definition osd.c:765
int Height(void) const
Definition osd.h:189
bool Dirty(int &x1, int &y1, int &x2, int &y2)
Tells whether there is a dirty area and returns the bounding rectangle of that area (relative to the ...
Definition osd.c:342
cBitmap * Scaled(double FactorX, double FactorY, bool AntiAlias=false) const
Creates a copy of this bitmap, scaled by the given factors.
Definition osd.c:838
int X0(void) const
Definition osd.h:186
tColor GetColor(int x, int y) const
Returns the color at the given coordinates.
Definition osd.h:277
void SetIndex(int x, int y, tIndex Index)
Sets the index at the given coordinates to Index.
Definition osd.c:500
void DrawRectangle(int x1, int y1, int x2, int y2, tColor Color)
Draws a filled rectangle defined by the upper left (x1, y1) and lower right (x2, y2) corners with the...
Definition osd.c:611
void Clean(void)
Marks the dirty area as clean.
Definition osd.c:354
void DrawBitmap(int x, int y, const cBitmap &Bitmap, tColor ColorFg=0, tColor ColorBg=0, bool ReplacePalette=false, bool Overlay=false)
Sets the pixels in this bitmap with the data from the given Bitmap, putting the upper left corner of ...
Definition osd.c:533
void Fill(tIndex Index)
Fills the bitmap data with the given Index.
Definition osd.c:515
void DrawText(int x, int y, const char *s, tColor ColorFg, tColor ColorBg, const cFont *Font, int Width=0, int Height=0, int Alignment=taDefault)
Draws the given string at coordinates (x, y) with the given foreground and background color and font.
Definition osd.c:562
int Y0(void) const
Definition osd.h:187
int Width(void) const
Definition osd.h:188
static const char * SystemCharacterTable(void)
Definition tools.h:174
const char * Convert(const char *From, char *To=NULL, size_t ToLength=0)
Converts the given Text from FromCode to ToCode (as set in the constructor).
Definition tools.c:1009
static void SleepMs(int TimeoutMs)
Creates a cCondWait object and uses it to sleep for TimeoutMs milliseconds, immediately giving up the...
Definition thread.c:72
virtual void GetVideoSize(int &Width, int &Height, double &VideoAspect)
Returns the Width, Height and VideoAspect ratio of the currently displayed video material.
Definition device.c:533
static cDevice * PrimaryDevice(void)
Returns the primary device.
Definition device.h:148
virtual int64_t GetSTC(void)
Gets the current System Time Counter, which can be used to synchronize audio, video and subtitles.
Definition device.c:1266
virtual void GetOsdSize(int &Width, int &Height, double &PixelAspect)
Returns the Width, Height and PixelAspect ratio the OSD should use to best fit the resolution of the ...
Definition device.c:540
virtual ~cDvbSubtitleAssembler()
void Put(const uchar *Data, int Length)
bool Realloc(int Size)
unsigned char * Get(int &Length)
cDvbSubtitleBitmaps(int State, int64_t Pts, int Timeout, tArea *Areas, int NumAreas, double OsdFactorX, double OsdFactorY, tArea &AreaCombined, tArea &AreaOsd)
void Draw(cOsd *Osd)
int64_t Pts(void)
void DbgDump(int WindowWidth, int WindowHeight)
void AddBitmap(cBitmap *Bitmap)
cVector< cBitmap * > bitmaps
int Convert(const uchar *Data, int Length)
cList< cDvbSubtitleBitmaps > * bitmaps
Definition dvbsubtitle.h:41
void FinishPage(cDvbSubtitlePage *Page)
virtual void Action(void)
A derived cThread class must implement the code it wants to execute as a separate thread in this func...
int ExtractPgsSegment(const uchar *Data, int Length, int64_t Pts)
int ExtractSegment(const uchar *Data, int Length, int64_t Pts)
cDvbSubtitleAssembler * dvbSubtitleAssembler
Definition dvbsubtitle.h:26
cList< cDvbSubtitlePage > * pages
Definition dvbsubtitle.h:40
cDvbSubtitlePage * GetPageById(int PageId, bool New=false)
virtual ~cDvbSubtitleConverter()
static void SetupChanged(void)
int ConvertFragments(const uchar *Data, int Length)
cSubtitleRegionRef * GetRegionRefByIndex(int RegionRefIndex)
bool Pending(void)
cSubtitleObjects objects
cList< cSubtitleRegion > regions
cSubtitleClut * GetClutById(int ClutId, bool New=false)
void SetPending(bool Pending)
cDvbSubtitlePage(int PageId)
void Parse(int64_t Pts, cBitStream &bs)
int PageState(void)
int PageTimeout(void)
cList< cSubtitleClut > cluts
tArea CombineAreas(int NumAreas, const tArea *Areas)
tArea * GetAreas(int &NumAreas)
int PageVersionNumber(void)
cSubtitleObjects * Objects(void)
cSubtitleObject * GetObjectById(int ObjectId, bool New=false)
void AddRegionRef(cSubtitleRegionRef *rf)
cList< cSubtitleRegionRef > regionRefs
void ParsePgs(int64_t Pts, cBitStream &bs)
cSubtitleRegion * GetRegionById(int RegionId, bool New=false)
tArea ScaleArea(const tArea &Area, double FactorX, double FactorY)
int64_t Pts(void) const
Definition font.h:37
virtual int Width(void) const =0
Returns the original character width as requested when the font was created, or 0 if the default widt...
static cFont * CreateFont(const char *Name, int CharHeight, int CharWidth=0)
Creates a new font object with the given Name and makes its characters CharHeight pixels high.
Definition font.c:429
virtual int Height(void) const =0
Returns the height of this font in pixel (all characters have the same height).
virtual void Clear(void)
Definition tools.c:2291
void Del(cListObject *Object, bool DeleteObject=true)
Definition tools.c:2246
int Count(void) const
Definition tools.h:640
void Add(cListObject *Object, cListObject *After=NULL)
Definition tools.c:2214
int Index(void) const
Definition tools.c:2134
Definition tools.h:644
const cSubtitleObject * First(void) const
Returns the first element in this list, or NULL if the list is empty.
Definition tools.h:656
const cSubtitleObject * Next(const cSubtitleObject *Object) const
< Returns the element immediately before Object in this list, or NULL if Object is the first element ...
Definition tools.h:663
const T * Get(int Index) const
Returns the list element at the given Index, or NULL if no such element exists.
Definition tools.h:653
static cOsd * NewOsd(int Left, int Top, uint Level=OSD_LEVEL_DEFAULT)
Returns a pointer to a newly created cOsd object, which will be located at the given coordinates.
Definition osd.c:2290
The cOsd class is the interface to the "On Screen Display".
Definition osd.h:753
virtual eOsdError SetAreas(const tArea *Areas, int NumAreas)
Sets the sub-areas to the given areas.
Definition osd.c:2092
virtual eOsdError CanHandleAreas(const tArea *Areas, int NumAreas)
Checks whether the OSD can display the given set of sub-areas.
Definition osd.c:2070
virtual void DrawScaledBitmap(int x, int y, const cBitmap &Bitmap, double FactorX, double FactorY, bool AntiAlias=false)
Sets the pixels in the OSD with the data from the given Bitmap, putting the upper left corner of the ...
Definition osd.c:2216
virtual void Flush(void)
Actually commits all data to the OSD hardware.
Definition osd.c:2266
Definition osd.h:88
tColor Color(int Index) const
Returns the color at the given Index.
Definition osd.h:119
void Replace(const cPalette &Palette)
Replaces the colors of this palette with the colors from the given palette.
Definition osd.c:208
void SetColor(int Index, tColor Color)
Sets the palette entry at Index to Color.
Definition osd.c:172
int Bpp(void) const
Definition osd.h:111
int SubtitleFgTransparency
Definition config.h:296
int AntiAlias
Definition config.h:336
int FontOsdSize
Definition config.h:343
int SubtitleOffset
Definition config.h:295
int SubtitleBgTransparency
Definition config.h:296
char FontOsd[MAXFONTNAME]
Definition config.h:337
static cString sprintf(const char *fmt,...) __attribute__((format(printf
Definition tools.c:1175
void Parse(cBitStream &bs)
int ClutId(void)
cPalette palette4
cPalette palette2
tColor yuv2rgb(int Y, int Cb, int Cr)
void SetColor(int Bpp, int Index, tColor Color)
int ClutVersionNumber(void)
const cPalette * GetPalette(int Bpp)
cSubtitleClut(int ClutId)
void ParsePgs(cBitStream &bs)
cPalette palette8
cString WriteJpeg(const cBitmap *Bitmap, int MaxX=0, int MaxY=0)
Definition dvbsubtitle.c:92
void SetFactor(double Factor)
Definition dvbsubtitle.c:79
bool Active(void)
Definition dvbsubtitle.c:76
int64_t FirstPts(void)
Definition dvbsubtitle.c:77
cSubtitleDebug(void)
Definition dvbsubtitle.c:74
void WriteHtml(const char *Format,...)
void Reset(void)
Definition dvbsubtitle.c:84
void SetFirstPts(int64_t FirstPts)
Definition dvbsubtitle.c:78
int64_t firstPts
Definition dvbsubtitle.c:70
cSubtitleObjectRefPgs(cBitStream &bs)
int ObjectProviderFlag(void)
int ForegroundPixelCode(void)
int ObjectVerticalPosition(void)
int ObjectHorizontalPosition(void)
int BackgroundPixelCode(void)
cSubtitleObject(int ObjectId)
int ObjectId(void)
bool Decode4BppCodeString(cBitmap *Bitmap, int px, int py, cBitStream *bs, int &x, int y, const uint8_t *MapTable)
void DecodeCharacterString(const uchar *Data, int NumberOfCodes)
bool Decode2BppCodeString(cBitmap *Bitmap, int px, int py, cBitStream *bs, int &x, int y, const uint8_t *MapTable)
bool Decode8BppCodeString(cBitmap *Bitmap, int px, int py, cBitStream *bs, int &x, int y)
void DrawLine(cBitmap *Bitmap, int x, int y, tIndex Index, int Length)
void ParsePgs(cBitStream &bs)
bool nonModifyingColorFlag
void DecodeSubBlock(cBitmap *Bitmap, int px, int py, const uchar *Data, int Length, bool Even)
void Render(cBitmap *Bitmap, int px, int py, tIndex IndexFg, tIndex IndexBg)
int ObjectVersionNumber(void)
bool DecodePgsCodeString(cBitmap *Bitmap, int px, int py, cBitStream *bs, int &x, int y)
int ObjectCodingMethod(void)
void Parse(cBitStream &bs)
bool NonModifyingColorFlag(void)
cSubtitleObject * GetObjectById(int ObjectId, bool New=false)
cSubtitleRegionRef(int id, int x, int y)
int RegionHorizontalAddress(void)
int RegionVerticalAddress(void)
int ClutId(void)
cList< cSubtitleObjectRef > objectRefs
int RegionWidth(void)
int regionLevelOfCompatibility
void Parse(cBitStream &bs)
int RegionHeight(void)
bool RegionFillFlag(void)
void SetDimensions(int Width, int Height)
int RegionId(void)
int RegionDepth(void)
void Render(cBitmap *Bitmap, cSubtitleObjects *Objects)
int RegionVersionNumber(void)
cSubtitleRegion(int RegionId)
void ParsePgs(cBitStream &bs)
int RegionLevelOfCompatibility(void)
void Unlock(void)
Definition thread.h:95
void bool Start(void)
Sets the description of this thread, which will be used when logging starting or stopping of the thre...
Definition thread.c:304
bool Running(void)
Returns false if a derived cThread object shall leave its Action() function.
Definition thread.h:101
void Lock(void)
Definition thread.h:94
void Cancel(int WaitSeconds=0)
Cancels the thread by first setting 'running' to false, so that the Action() loop can finish in an or...
Definition thread.c:354
void Set(int Ms=0)
Sets the timer.
Definition tools.c:792
bool TimedOut(void) const
Definition tools.c:797
int Size(void) const
Definition tools.h:767
virtual void Append(T Data)
Definition tools.h:787
cSetup Setup
Definition config.c:372
#define PGS_PALETTE_SEGMENT
Definition dvbsubtitle.c:28
#define DBGBITMAPWIDTH
Definition dvbsubtitle.c:60
#define OBJECT_DATA_SEGMENT
Definition dvbsubtitle.c:22
#define dbgpages(a...)
Definition dvbsubtitle.c:50
#define DBGBACKGROUND
#define LimitTo32Bit(n)
#define PGS_OBJECT_SEGMENT
Definition dvbsubtitle.c:29
static bool DebugNormal
Definition dvbsubtitle.c:37
#define CLUT_DEFINITION_SEGMENT
Definition dvbsubtitle.c:21
#define dbgsegments(a...)
Definition dvbsubtitle.c:54
static bool DebugSegments
Definition dvbsubtitle.c:44
#define END_OF_DISPLAY_SET_SEGMENT
Definition dvbsubtitle.c:25
static bool DebugVerbose
Definition dvbsubtitle.c:38
#define dbgpixel(a...)
Definition dvbsubtitle.c:55
#define dbgcluts(a...)
Definition dvbsubtitle.c:56
#define dbgregions(a...)
Definition dvbsubtitle.c:51
#define REGION_COMPOSITION_SEGMENT
Definition dvbsubtitle.c:20
#define DBGMAXBITMAPS
Definition dvbsubtitle.c:59
static bool DebugCluts
Definition dvbsubtitle.c:46
#define BORDER
#define dbgdisplay(a...)
Definition dvbsubtitle.c:49
#define PAGE_COMPOSITION_SEGMENT
Definition dvbsubtitle.c:19
#define STUFFING_SEGMENT
Definition dvbsubtitle.c:26
#define PGS_WINDOW_SEGMENT
Definition dvbsubtitle.c:31
#define dbgobjects(a...)
Definition dvbsubtitle.c:52
static bool DebugRegions
Definition dvbsubtitle.c:41
static bool DebugDisplay
Definition dvbsubtitle.c:39
#define PGS_DISPLAY_SEGMENT
Definition dvbsubtitle.c:32
static bool DebugConverter
Definition dvbsubtitle.c:43
static bool DebugObjects
Definition dvbsubtitle.c:42
static cSubtitleDebug SD
#define DISPARITY_SIGNALING_SEGMENT
Definition dvbsubtitle.c:24
#define dbgconverter(a...)
Definition dvbsubtitle.c:53
#define PGS_PRESENTATION_SEGMENT
Definition dvbsubtitle.c:30
static bool DebugPixel
Definition dvbsubtitle.c:45
#define DISPLAY_DEFINITION_SEGMENT
Definition dvbsubtitle.c:23
static bool DebugOutput
Definition dvbsubtitle.c:47
static bool DebugPages
Definition dvbsubtitle.c:40
#define dbgoutput(a...)
Definition dvbsubtitle.c:57
uint32_t tColor
Definition font.h:30
uint8_t tIndex
Definition font.h:31
const char * getCharacterTable(const unsigned char *&buffer, int &length, bool *isSingleByte)
Definition si.c:364
#define OSD_LEVEL_SUBTITLES
Definition osd.h:22
@ oeOk
Definition osd.h:44
tColor ArgbToColor(uint8_t A, uint8_t R, uint8_t G, uint8_t B)
Definition osd.h:58
int PesPayloadOffset(const uchar *p)
Definition remux.h:178
bool PesHasPts(const uchar *p)
Definition remux.h:183
int64_t PesGetPts(const uchar *p)
Definition remux.h:193
Definition osd.h:298
int Width(void) const
Definition osd.h:301
int bpp
Definition osd.h:300
int x2
Definition osd.h:299
int y1
Definition osd.h:299
int x1
Definition osd.h:299
int Height(void) const
Definition osd.h:302
int y2
Definition osd.h:299
#define LOCK_THREAD
Definition thread.h:167
uchar * RgbToJpeg(uchar *Mem, int Width, int Height, int &Size, int Quality)
Converts the given Memory to a JPEG image and returns a pointer to the resulting image.
Definition tools.c:1346
T constrain(T v, T l, T h)
Definition tools.h:70
#define LOG_ERROR_STR(s)
Definition tools.h:40
unsigned char uchar
Definition tools.h:31
#define MALLOC(type, size)
Definition tools.h:47
void DELETENULL(T *&p)
Definition tools.h:49
bool DoubleEqual(double a, double b)
Definition tools.h:97
T min(T a, T b)
Definition tools.h:63
T max(T a, T b)
Definition tools.h:64
#define esyslog(a...)
Definition tools.h:35