vdr 2.6.1
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.1 2021/03/17 15:24:34 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
750protected:
758public:
759 cSubtitleObjectRef(void);
761 int ObjectId(void) { return objectId; }
762 int ObjectType(void) { return objectType; }
768 };
769
771{
772 objectId = 0;
773 objectType = 0;
779}
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
1193{
1194 free(data);
1195}
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
1383{
1384 setupLevel++;
1385}
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 tArea AreaCombined = Page->CombineAreas(NumAreas, Areas);
1774 tArea AreaOsd = Page->ScaleArea(AreaCombined, osdFactorX, osdFactorY);
1775 int Bpp = 8;
1776 bool Reduced = false;
1777 if (osd && NumAreas > 0) {
1778 while (osd->CanHandleAreas(&AreaOsd, 1) != oeOk) {
1779 dbgoutput("CanHandleAreas: %d<br>\n", osd->CanHandleAreas(&AreaOsd, 1));
1780 int HalfBpp = Bpp / 2;
1781 if (HalfBpp >= 2) {
1782 if (AreaOsd.bpp >= Bpp) {
1783 AreaOsd.bpp = HalfBpp;
1784 Reduced = true;
1785 }
1786 Bpp = HalfBpp;
1787 }
1788 else
1789 return; // unable to draw bitmaps
1790 }
1791 }
1792 cDvbSubtitleBitmaps *Bitmaps = new cDvbSubtitleBitmaps(Page->PageState(), Page->Pts(), Page->PageTimeout(), Areas, NumAreas, osdFactorX, osdFactorY, AreaCombined, AreaOsd);
1793 bitmaps->Add(Bitmaps);
1794 for (int i = 0; i < NumAreas; i++) {
1795 if (cSubtitleRegionRef *srr = Page->GetRegionRefByIndex(i)) {
1796 if (cSubtitleRegion *sr = Page->GetRegionById(srr->RegionId())) {
1797 if (cSubtitleClut *clut = Page->GetClutById(sr->ClutId())) {
1798 cBitmap *bm = new cBitmap(sr->RegionWidth(), sr->RegionHeight(), sr->RegionDepth());
1799 bm->Replace(*clut->GetPalette(sr->RegionDepth()));
1800 sr->Render(bm, Page->Objects());
1801 if (Reduced) {
1802 if (sr->RegionDepth() != Areas[i].bpp) {
1803 if (sr->RegionLevelOfCompatibility() <= Areas[i].bpp) {
1804 //TODO this is untested - didn't have any such subtitle stream
1805 cSubtitleClut *Clut = Page->GetClutById(sr->ClutId());
1806 dbgregions("reduce region %d bpp %d level %d area bpp %d<br>\n", sr->RegionId(), sr->RegionDepth(), sr->RegionLevelOfCompatibility(), Areas[i].bpp);
1807 bm->ReduceBpp(*Clut->GetPalette(sr->RegionDepth()));
1808 }
1809 else {
1810 dbgregions("condense region %d bpp %d level %d area bpp %d<br>\n", sr->RegionId(), sr->RegionDepth(), sr->RegionLevelOfCompatibility(), Areas[i].bpp);
1811 bm->ShrinkBpp(Areas[i].bpp);
1812 }
1813 }
1814 }
1815 bm->SetOffset(srr->RegionHorizontalAddress(), srr->RegionVerticalAddress());
1816 Bitmaps->AddBitmap(bm);
1817 }
1818 }
1819 }
1820 }
1821 if (DebugPages)
1822 Bitmaps->DbgDump(windowWidth, windowHeight);
1823}
void SkipBit(void)
int Index(void) const
void WordAlign(void)
Definition: tools.c:1453
bool SetLength(int Length)
Definition: tools.c:1460
int Length(void) const
bool IsEOF(void) const
const uint8_t * GetData(void) const
void SkipBits(int n)
uint32_t GetBits(int n)
Definition: tools.c:1438
void ByteAlign(void)
Definition: tools.c:1446
int GetBit(void)
Definition: tools.c:1429
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.
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
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
tColor GetColor(int x, int y) const
Returns the color at the given coordinates.
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
int Width(void) const
static const char * SystemCharacterTable(void)
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:525
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:1244
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:532
virtual ~cDvbSubtitleAssembler()
Definition: dvbsubtitle.c:1192
void Put(const uchar *Data, int Length)
Definition: dvbsubtitle.c:1236
bool Realloc(int Size)
Definition: dvbsubtitle.c:1203
unsigned char * Get(int &Length)
Definition: dvbsubtitle.c:1223
cDvbSubtitleBitmaps(int State, int64_t Pts, int Timeout, tArea *Areas, int NumAreas, double OsdFactorX, double OsdFactorY, tArea &AreaCombined, tArea &AreaOsd)
Definition: dvbsubtitle.c:1270
void Draw(cOsd *Osd)
Definition: dvbsubtitle.c:1295
int64_t Pts(void)
Definition: dvbsubtitle.c:1262
bool HasBitmaps(void)
Definition: dvbsubtitle.c:1265
void DbgDump(int WindowWidth, int WindowHeight)
Definition: dvbsubtitle.c:1321
void AddBitmap(cBitmap *Bitmap)
Definition: dvbsubtitle.c:1290
cVector< cBitmap * > bitmaps
Definition: dvbsubtitle.c:1257
int Convert(const uchar *Data, int Length)
Definition: dvbsubtitle.c:1450
static int setupLevel
Definition: dvbsubtitle.h:25
void FinishPage(cDvbSubtitlePage *Page)
Definition: dvbsubtitle.c:1767
virtual void Action(void)
A derived cThread class must implement the code it wants to execute as a separate thread in this func...
Definition: dvbsubtitle.c:1488
int ExtractPgsSegment(const uchar *Data, int Length, int64_t Pts)
Definition: dvbsubtitle.c:1702
int ExtractSegment(const uchar *Data, int Length, int64_t Pts)
Definition: dvbsubtitle.c:1582
cDvbSubtitleAssembler * dvbSubtitleAssembler
Definition: dvbsubtitle.h:26
cList< cDvbSubtitleBitmaps > * bitmaps
Definition: dvbsubtitle.h:41
cDvbSubtitlePage * GetPageById(int PageId, bool New=false)
Definition: dvbsubtitle.c:1569
virtual ~cDvbSubtitleConverter()
Definition: dvbsubtitle.c:1373
cList< cDvbSubtitlePage > * pages
Definition: dvbsubtitle.h:40
static void SetupChanged(void)
Definition: dvbsubtitle.c:1382
int ConvertFragments(const uchar *Data, int Length)
Definition: dvbsubtitle.c:1404
cSubtitleRegionRef * GetRegionRefByIndex(int RegionRefIndex)
Definition: dvbsubtitle.c:1002
int PageId(void)
Definition: dvbsubtitle.c:989
bool Pending(void)
Definition: dvbsubtitle.c:994
cSubtitleObjects objects
Definition: dvbsubtitle.c:981
cList< cSubtitleRegion > regions
Definition: dvbsubtitle.c:983
cSubtitleClut * GetClutById(int ClutId, bool New=false)
Definition: dvbsubtitle.c:1137
void SetPending(bool Pending)
Definition: dvbsubtitle.c:1004
cDvbSubtitlePage(int PageId)
Definition: dvbsubtitle.c:1007
void Parse(int64_t Pts, cBitStream &bs)
Definition: dvbsubtitle.c:1017
int PageState(void)
Definition: dvbsubtitle.c:992
int PageTimeout(void)
Definition: dvbsubtitle.c:990
cList< cSubtitleClut > cluts
Definition: dvbsubtitle.c:982
tArea CombineAreas(int NumAreas, const tArea *Areas)
Definition: dvbsubtitle.c:1106
tArea * GetAreas(int &NumAreas)
Definition: dvbsubtitle.c:1082
int PageVersionNumber(void)
Definition: dvbsubtitle.c:991
cSubtitleObjects * Objects(void)
Definition: dvbsubtitle.c:995
cSubtitleObject * GetObjectById(int ObjectId, bool New=false)
Definition: dvbsubtitle.c:1163
void AddRegionRef(cSubtitleRegionRef *rf)
Definition: dvbsubtitle.c:1003
cList< cSubtitleRegionRef > regionRefs
Definition: dvbsubtitle.c:984
void ParsePgs(int64_t Pts, cBitStream &bs)
Definition: dvbsubtitle.c:1053
cSubtitleRegion * GetRegionById(int RegionId, bool New=false)
Definition: dvbsubtitle.c:1150
tArea ScaleArea(const tArea &Area, double FactorX, double FactorY)
Definition: dvbsubtitle.c:1124
int64_t Pts(void) const
Definition: dvbsubtitle.c:993
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:428
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:2261
void Del(cListObject *Object, bool DeleteObject=true)
Definition: tools.c:2216
int Count(void) const
void Add(cListObject *Object, cListObject *After=NULL)
Definition: tools.c:2184
int Index(void) const
Definition: tools.c:2104
const cSubtitleObject * First(void) const
Returns the first element in this list, or NULL if the list is empty.
const cSubtitleObject * Next(const cSubtitleObject *Object) const
< Returns the element immediately before Object in this list, or NULL if Object is the first element ...
const T * Get(int Index) const
Returns the list element at the given Index, or NULL if no such element exists.
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:2215
The cOsd class is the interface to the "On Screen Display".
virtual eOsdError SetAreas(const tArea *Areas, int NumAreas)
Sets the sub-areas to the given areas.
Definition: osd.c:2029
virtual eOsdError CanHandleAreas(const tArea *Areas, int NumAreas)
Checks whether the OSD can display the given set of sub-areas.
Definition: osd.c:2007
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:2141
virtual void Flush(void)
Actually commits all data to the OSD hardware.
Definition: osd.c:2191
tColor Color(int Index) const
Returns the color at the given Index.
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
int SubtitleFgTransparency
Definition: config.h:296
int AntiAlias
Definition: config.h:334
int FontOsdSize
Definition: config.h:341
int SubtitleOffset
Definition: config.h:295
int SubtitleBgTransparency
Definition: config.h:296
char FontOsd[MAXFONTNAME]
Definition: config.h:335
static cString sprintf(const char *fmt,...) __attribute__((format(printf
Definition: tools.c:1149
void Parse(cBitStream &bs)
Definition: dvbsubtitle.c:230
int ClutId(void)
Definition: dvbsubtitle.c:156
int clutVersionNumber
Definition: dvbsubtitle.c:146
cPalette palette4
Definition: dvbsubtitle.c:148
cPalette palette2
Definition: dvbsubtitle.c:147
tColor yuv2rgb(int Y, int Cb, int Cr)
Definition: dvbsubtitle.c:302
void SetColor(int Bpp, int Index, tColor Color)
Definition: dvbsubtitle.c:318
int ClutVersionNumber(void)
Definition: dvbsubtitle.c:157
const cPalette * GetPalette(int Bpp)
Definition: dvbsubtitle.c:328
cSubtitleClut(int ClutId)
Definition: dvbsubtitle.c:161
void ParsePgs(cBitStream &bs)
Definition: dvbsubtitle.c:277
cPalette palette8
Definition: dvbsubtitle.c:149
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,...)
Definition: dvbsubtitle.c:124
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)
Definition: dvbsubtitle.c:814
int ObjectType(void)
Definition: dvbsubtitle.c:762
int ObjectProviderFlag(void)
Definition: dvbsubtitle.c:763
int ForegroundPixelCode(void)
Definition: dvbsubtitle.c:766
int ObjectVerticalPosition(void)
Definition: dvbsubtitle.c:765
int ObjectHorizontalPosition(void)
Definition: dvbsubtitle.c:764
int BackgroundPixelCode(void)
Definition: dvbsubtitle.c:767
int ObjectId(void)
Definition: dvbsubtitle.c:761
cSubtitleObject(int ObjectId)
Definition: dvbsubtitle.c:373
int ObjectId(void)
Definition: dvbsubtitle.c:366
bool Decode4BppCodeString(cBitmap *Bitmap, int px, int py, cBitStream *bs, int &x, int y, const uint8_t *MapTable)
Definition: dvbsubtitle.c:611
void DecodeCharacterString(const uchar *Data, int NumberOfCodes)
Definition: dvbsubtitle.c:466
bool Decode2BppCodeString(cBitmap *Bitmap, int px, int py, cBitStream *bs, int &x, int y, const uint8_t *MapTable)
Definition: dvbsubtitle.c:571
bool Decode8BppCodeString(cBitmap *Bitmap, int px, int py, cBitStream *bs, int &x, int y)
Definition: dvbsubtitle.c:656
void DrawLine(cBitmap *Bitmap, int x, int y, tIndex Index, int Length)
Definition: dvbsubtitle.c:563
void ParsePgs(cBitStream &bs)
Definition: dvbsubtitle.c:443
bool nonModifyingColorFlag
Definition: dvbsubtitle.c:346
void DecodeSubBlock(cBitmap *Bitmap, int px, int py, const uchar *Data, int Length, bool Even)
Definition: dvbsubtitle.c:494
void Render(cBitmap *Bitmap, int px, int py, tIndex IndexFg, tIndex IndexBg)
Definition: dvbsubtitle.c:703
int ObjectVersionNumber(void)
Definition: dvbsubtitle.c:367
bool DecodePgsCodeString(cBitmap *Bitmap, int px, int py, cBitStream *bs, int &x, int y)
Definition: dvbsubtitle.c:681
int ObjectCodingMethod(void)
Definition: dvbsubtitle.c:368
void Parse(cBitStream &bs)
Definition: dvbsubtitle.c:395
bool NonModifyingColorFlag(void)
Definition: dvbsubtitle.c:369
cSubtitleObject * GetObjectById(int ObjectId, bool New=false)
Definition: dvbsubtitle.c:734
cSubtitleRegionRef(int id, int x, int y)
Definition: dvbsubtitle.c:955
int RegionId(void)
Definition: dvbsubtitle.c:950
int RegionHorizontalAddress(void)
Definition: dvbsubtitle.c:951
int RegionVerticalAddress(void)
Definition: dvbsubtitle.c:952
int ClutId(void)
Definition: dvbsubtitle.c:860
cList< cSubtitleObjectRef > objectRefs
Definition: dvbsubtitle.c:847
int RegionWidth(void)
Definition: dvbsubtitle.c:856
int regionLevelOfCompatibility
Definition: dvbsubtitle.c:841
void Parse(cBitStream &bs)
Definition: dvbsubtitle.c:879
int RegionHeight(void)
Definition: dvbsubtitle.c:857
bool RegionFillFlag(void)
Definition: dvbsubtitle.c:855
void SetDimensions(int Width, int Height)
Definition: dvbsubtitle.c:916
int RegionId(void)
Definition: dvbsubtitle.c:853
int RegionDepth(void)
Definition: dvbsubtitle.c:859
void Render(cBitmap *Bitmap, cSubtitleObjects *Objects)
Definition: dvbsubtitle.c:923
int RegionVersionNumber(void)
Definition: dvbsubtitle.c:854
cSubtitleRegion(int RegionId)
Definition: dvbsubtitle.c:864
void ParsePgs(cBitStream &bs)
Definition: dvbsubtitle.c:905
int RegionLevelOfCompatibility(void)
Definition: dvbsubtitle.c:858
void Unlock(void)
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.
void Lock(void)
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
virtual void Append(T Data)
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)
Definition: dvbsubtitle.c:1486
#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
Definition: dvbsubtitle.c:139
#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
#define OSD_LEVEL_SUBTITLES
@ oeOk
tColor ArgbToColor(uint8_t A, uint8_t R, uint8_t G, uint8_t B)
int PesPayloadOffset(const uchar *p)
bool PesHasPts(const uchar *p)
int64_t PesGetPts(const uchar *p)
#define LOCK_THREAD
T constrain(T v, T l, T h)
#define LOG_ERROR_STR(s)
unsigned char uchar
uchar * RgbToJpeg(uchar *Mem, int Width, int Height, int &Size, int Quality=100)
Converts the given Memory to a JPEG image and returns a pointer to the resulting image.
Definition: tools.c:1320
#define MALLOC(type, size)
void DELETENULL(T *&p)
bool DoubleEqual(double a, double b)
T min(T a, T b)
T max(T a, T b)
#define esyslog(a...)
const char * getCharacterTable(const unsigned char *&buffer, int &length, bool *isSingleByte=NULL)
Definition: si.c:364
int Width(void) const
int Height(void) const