vdr 2.7.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.6 2025/04/05 10:16:18 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?
715 cFont *font = cFont::CreateFont(Setup.FontOsd, Setup.FontOsdSize);
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
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())
902 objectRefs.Add(new cSubtitleObjectRef(bs));
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())
1049 regionRefs.Add(new cSubtitleRegionRef(bs));
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);
1260 virtual ~cDvbSubtitleBitmaps() override;
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) {
1309 cBitmap combined(areaCombined.Width(), areaCombined.Height(), areaCombined.bpp);
1310 combined.SetOffset(areaCombined.x1, areaCombined.y1);
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
1354#define SUBTITLE_RETENTION 120 // seconds
1355
1357
1359:cThread("subtitle converter")
1360{
1362 osd = NULL;
1363 frozen = false;
1364 ddsVersionNumber = -1;
1365 displayWidth = windowWidth = 720;
1369 osdDeltaX = osdDeltaY = 0;
1370 osdFactorX = osdFactorY = 1.0;
1372 visible = true;
1373 endVisible = -1;
1376 current = NULL;
1377 SD.Reset();
1378 Start();
1379}
1380
1382{
1383 Cancel(3);
1384 delete dvbSubtitleAssembler;
1385 delete osd;
1386 delete bitmaps;
1387 delete pages;
1388}
1389
1391{
1393 visible = Visible;
1394 if (!visible)
1395 DELETENULL(osd);
1396 endVisible = -1;
1397}
1398
1400{
1402 if (!visible) {
1404 visible = true;
1405 }
1406}
1407
1412
1414{
1415 dbgconverter("converter reset -----------------------<br>\n");
1416 dvbSubtitleAssembler->Reset();
1417 Lock();
1418 pages->Clear();
1419 current = NULL;
1420 DELETENULL(osd);
1421 frozen = false;
1422 ddsVersionNumber = -1;
1423 Unlock();
1424}
1425
1427{
1428 if (Data && Length > 8) {
1429 int PayloadOffset = PesPayloadOffset(Data);
1430 int SubstreamHeaderLength = 4;
1431 bool ResetSubtitleAssembler = Data[PayloadOffset + 3] == 0x00;
1432
1433 // Compatibility mode for old subtitles plugin:
1434 if ((Data[7] & 0x01) && (Data[PayloadOffset - 3] & 0x81) == 0x01 && Data[PayloadOffset - 2] == 0x81) {
1435 PayloadOffset--;
1436 SubstreamHeaderLength = 1;
1437 ResetSubtitleAssembler = Data[8] >= 5;
1438 }
1439
1440 if (Length > PayloadOffset + SubstreamHeaderLength) {
1441 int64_t pts = PesHasPts(Data) ? PesGetPts(Data) : -1;
1442 if (pts >= 0)
1443 dbgconverter("converter PTS: %" PRId64 "<br>\n", pts);
1444 const uchar *data = Data + PayloadOffset + SubstreamHeaderLength; // skip substream header
1445 int length = Length - PayloadOffset - SubstreamHeaderLength; // skip substream header
1446 if (ResetSubtitleAssembler)
1447 dvbSubtitleAssembler->Reset();
1448
1449 if (length > 3) {
1450 if (data[0] == 0x20 && data[1] == 0x00 && data[2] == 0x0F)
1451 dvbSubtitleAssembler->Put(data + 2, length - 2);
1452 else
1453 dvbSubtitleAssembler->Put(data, length);
1454
1455 int Count;
1456 while (true) {
1457 unsigned char *b = dvbSubtitleAssembler->Get(Count);
1458 if (b && b[0] == 0x0F) {
1459 if (ExtractSegment(b, Count, pts) == -1)
1460 break;
1461 }
1462 else
1463 break;
1464 }
1465 }
1466 }
1467 return Length;
1468 }
1469 return 0;
1470}
1471
1472int cDvbSubtitleConverter::Convert(const uchar *Data, int Length)
1473{
1474 if (Data && Length > 8) {
1475 int PayloadOffset = PesPayloadOffset(Data);
1476 if (Length > PayloadOffset) {
1477 int64_t pts = PesHasPts(Data) ? PesGetPts(Data) : -1;
1478 if (pts >= 0)
1479 dbgconverter("converter PTS: %" PRId64 "<br>\n", pts);
1480 const uchar *data = Data + PayloadOffset;
1481 int length = Length - PayloadOffset;
1482 if (length > 0) {
1483 if (length > 2 && data[0] == 0x20 && data[1] == 0x00 && data[2] == 0x0F) {
1484 data += 2;
1485 length -= 2;
1486 }
1487 const uchar *b = data;
1488 while (length > 0) {
1489 if (b[0] == STUFFING_SEGMENT)
1490 break;
1491 int n;
1492 if (b[0] == 0x0F)
1493 n = ExtractSegment(b, length, pts);
1494 else
1495 n = ExtractPgsSegment(b, length, pts);
1496 if (n < 0)
1497 break;
1498 b += n;
1499 length -= n;
1500 }
1501 }
1502 }
1503 return Length;
1504 }
1505 return 0;
1506}
1507
1508#define LimitTo32Bit(n) ((n) & 0x00000000FFFFFFFFL)
1509
1510static int PtsDeltaMs(int64_t a, int64_t b)
1511{
1512 return (LimitTo32Bit(a) - LimitTo32Bit(b)) / 90; // some devices only deliver 32 bits, PTS are in 1/90000s
1513}
1514
1515#define TEMPSUBTITLETAIL 1000 // ms
1516
1518{
1519 int LastSetupLevel = setupLevel;
1520 while (Running()) {
1521 int WaitMs = 100;
1522 if (!frozen) {
1524 if (osd) {
1525 int NewSetupLevel = setupLevel;
1526 if (LastSetupLevel != NewSetupLevel) {
1527 dbgoutput("closing osd<br>\n");
1528 DELETENULL(osd);
1529 }
1530 LastSetupLevel = NewSetupLevel;
1531 }
1532 int64_t STC = cDevice::PrimaryDevice()->GetSTC();
1533 if (osd && current && current->Timeout() * 1000 - PtsDeltaMs(STC, current->Pts()) <= 0)
1534 DELETENULL(osd);
1535 for (cDvbSubtitleBitmaps *sb = bitmaps->First(); sb; ) {
1536 // Calculate the Delta between the STC (the current timestamp of the video)
1537 // and the bitmap's PTS (the timestamp when the bitmap shall be presented).
1538 // A negative Delta means that the bitmap will be presented in the future:
1539 cDvbSubtitleBitmaps *This = NULL;
1540 int Delta = PtsDeltaMs(STC, sb->Pts());
1541 if (Delta > 0 && sb == bitmaps->Last())
1542 Delta = 0; // avoids sticky subtitles at longer pauses
1543 if (Delta <= 0) { // found the bitmap that shall be displayed next
1544 if (Delta < 0)
1545 This = bitmaps->Prev(sb);
1546 else if (Delta == 0)
1547 This = sb;
1548 if (This && This != current) {
1549 current = This;
1550 if (endVisible >= 0 && PtsDeltaMs(STC, endVisible) > TEMPSUBTITLETAIL) {
1551 visible = false;
1552 endVisible = -1;
1553 DELETENULL(osd);
1554 }
1555 else if (!current->HasBitmaps() || current->Timeout() * 1000 - PtsDeltaMs(STC, current->Pts()) <= 0)
1556 DELETENULL(osd);
1557 else if (visible && AssertOsd()) {
1558 dbgoutput("showing bitmap #%d of %d<br>\n", current->Index() + 1, bitmaps->Count());
1559 current->Draw(osd);
1560 dbgconverter("PTS: %" PRId64 " STC: %" PRId64 " (%" PRId64 ") timeout: %d<br>\n", current->Pts(), STC, Delta, current->Timeout());
1561 }
1562 }
1563 break;
1564 }
1565 cDvbSubtitleBitmaps *sbOld = sb;
1566 sb = bitmaps->Next(sb);
1567 if (sbOld != current) {
1568 if (Delta > (sbOld->Timeout() + retention) * 1000)
1569 bitmaps->Del(sbOld);
1570 }
1571 }
1572 }
1573 cCondWait::SleepMs(WaitMs);
1574 }
1575}
1576
1578{
1579 int OsdWidth, OsdHeight;
1580 double OsdAspect;
1581 cDevice::PrimaryDevice()->GetOsdSize(OsdWidth, OsdHeight, OsdAspect);
1582 if (OsdWidth == displayWidth && OsdHeight == displayHeight) {
1583 osdFactorX = osdFactorY = 1.0;
1584 osdDeltaX = osdDeltaY = 0;
1585 }
1586 else {
1587 osdFactorX = osdFactorY = min(double(OsdWidth) / displayWidth, double(OsdHeight) / displayHeight);
1588 osdDeltaX = (OsdWidth - displayWidth * osdFactorX) / 2;
1589 osdDeltaY = (OsdHeight - displayHeight * osdFactorY) / 2;
1590 }
1591}
1592
1594{
1596 if (!osd) {
1597 SetOsdData();
1599 }
1600 return osd != NULL;
1601}
1602
1604{
1605 for (cDvbSubtitlePage *sp = pages->First(); sp; sp = pages->Next(sp)) {
1606 if (sp->PageId() == PageId)
1607 return sp;
1608 }
1609 if (!New)
1610 return NULL;
1611 cDvbSubtitlePage *Page = new cDvbSubtitlePage(PageId);
1612 pages->Add(Page);
1613 return Page;
1614}
1615
1616int cDvbSubtitleConverter::ExtractSegment(const uchar *Data, int Length, int64_t Pts)
1617{
1618 cBitStream bs(Data, Length * 8);
1619 if (Length > 5 && bs.GetBits(8) == 0x0F) { // sync byte
1620 int segmentType = bs.GetBits(8);
1621 if (segmentType == STUFFING_SEGMENT)
1622 return -1;
1624 cDvbSubtitlePage *page = GetPageById(bs.GetBits(16), true);
1625 int segmentLength = bs.GetBits(16);
1626 if (!bs.SetLength(bs.Index() + segmentLength * 8))
1627 return -1;
1628 switch (segmentType) {
1630 if (page->Pending()) {
1631 dbgsegments("END_OF_DISPLAY_SET_SEGMENT (simulated)<br>\n");
1632 FinishPage(page);
1633 }
1634 dbgsegments("PAGE_COMPOSITION_SEGMENT<br>\n");
1635 page->Parse(Pts, bs);
1636 SD.SetFactor(double(DBGBITMAPWIDTH) / windowWidth);
1637 break;
1638 }
1640 dbgsegments("REGION_COMPOSITION_SEGMENT<br>\n");
1641 cSubtitleRegion *region = page->GetRegionById(bs.GetBits(8), true);
1642 region->Parse(bs);
1643 break;
1644 }
1646 dbgsegments("CLUT_DEFINITION_SEGMENT<br>\n");
1647 cSubtitleClut *clut = page->GetClutById(bs.GetBits(8), true);
1648 clut->Parse(bs);
1649 break;
1650 }
1651 case OBJECT_DATA_SEGMENT: {
1652 dbgsegments("OBJECT_DATA_SEGMENT<br>\n");
1653 cSubtitleObject *object = page->GetObjectById(bs.GetBits(16), true);
1654 object->Parse(bs);
1655 break;
1656 }
1658 dbgsegments("DISPLAY_DEFINITION_SEGMENT<br>\n");
1659 int version = bs.GetBits(4);
1660#ifndef FIX_SUBTITLE_VERSION_BROADCASTER_STUPIDITY
1661 if (version == ddsVersionNumber)
1662 break; // no update
1663#endif
1664 bool displayWindowFlag = bs.GetBit();
1667 bs.SkipBits(3); // reserved
1668 displayWidth = windowWidth = bs.GetBits(16) + 1;
1669 displayHeight = windowHeight = bs.GetBits(16) + 1;
1670 if (displayWindowFlag) {
1671 windowHorizontalOffset = bs.GetBits(16); // displayWindowHorizontalPositionMinimum
1672 windowWidth = bs.GetBits(16) - windowHorizontalOffset + 1; // displayWindowHorizontalPositionMaximum
1673 windowVerticalOffset = bs.GetBits(16); // displayWindowVerticalPositionMinimum
1674 windowHeight = bs.GetBits(16) - windowVerticalOffset + 1; // displayWindowVerticalPositionMaximum
1675 }
1676 SetOsdData();
1677 ddsVersionNumber = version;
1678 dbgdisplay("<b>display</b> version %d flag %d width %d height %d ofshor %d ofsver %d<br>\n", ddsVersionNumber, displayWindowFlag, windowWidth, windowHeight, windowHorizontalOffset, windowVerticalOffset);
1679 break;
1680 }
1682 dbgsegments("DISPARITY_SIGNALING_SEGMENT<br>\n");
1683 bs.SkipBits(4); // dss_version_number
1684 bool disparity_shift_update_sequence_page_flag = bs.GetBit();
1685 bs.SkipBits(3); // reserved
1686 bs.SkipBits(8); // page_default_disparity_shift
1687 if (disparity_shift_update_sequence_page_flag) {
1688 bs.SkipBits(8); // disparity_shift_update_sequence_length
1689 bs.SkipBits(24); // interval_duration[23..0]
1690 int division_period_count = bs.GetBits(8);
1691 for (int i = 0; i < division_period_count; ++i) {
1692 bs.SkipBits(8); // interval_count
1693 bs.SkipBits(8); // disparity_shift_update_integer_part
1694 }
1695 }
1696 while (!bs.IsEOF()) {
1697 bs.SkipBits(8); // region_id
1698 bool disparity_shift_update_sequence_region_flag = bs.GetBit();
1699 bs.SkipBits(5); // reserved
1700 int number_of_subregions_minus_1 = bs.GetBits(2);
1701 for (int i = 0; i <= number_of_subregions_minus_1; ++i) {
1702 if (number_of_subregions_minus_1 > 0) {
1703 bs.SkipBits(16); // subregion_horizontal_position
1704 bs.SkipBits(16); // subregion_width
1705 }
1706 bs.SkipBits(8); // subregion_disparity_shift_integer_part
1707 bs.SkipBits(4); // subregion_disparity_shift_fractional_part
1708 bs.SkipBits(4); // reserved
1709 if (disparity_shift_update_sequence_region_flag) {
1710 bs.SkipBits(8); // disparity_shift_update_sequence_length
1711 bs.SkipBits(24); // interval_duration[23..0]
1712 int division_period_count = bs.GetBits(8);
1713 for (int i = 0; i < division_period_count; ++i) {
1714 bs.SkipBits(8); // interval_count
1715 bs.SkipBits(8); // disparity_shift_update_integer_part
1716 }
1717 }
1718 }
1719 }
1720 break;
1721 }
1723 dbgsegments("END_OF_DISPLAY_SET_SEGMENT<br>\n");
1724 FinishPage(page);
1725 page->SetPending(false);
1726 break;
1727 }
1728 default:
1729 dbgsegments("*** unknown segment type: %02X<br>\n", segmentType);
1730 }
1731 return bs.Length() / 8;
1732 }
1733 return -1;
1734}
1735
1736int cDvbSubtitleConverter::ExtractPgsSegment(const uchar *Data, int Length, int64_t Pts)
1737{
1738 cBitStream bs(Data, Length * 8);
1739 if (Length >= 3) {
1740 int segmentType = bs.GetBits(8);
1741 int segmentLength = bs.GetBits(16);
1742 if (!bs.SetLength(bs.Index() + segmentLength * 8))
1743 return -1;
1745 cDvbSubtitlePage *page = GetPageById(0, true);
1746 switch (segmentType) {
1748 if (page->Pending()) {
1749 dbgsegments("PGS_DISPLAY_SEGMENT (simulated)<br>\n");
1750 FinishPage(page);
1751 }
1752 dbgsegments("PGS_PRESENTATION_SEGMENT<br>\n");
1753 displayWidth = windowWidth = bs.GetBits(16);
1755 bs.SkipBits(8);
1756 page->ParsePgs(Pts, bs);
1757 SD.SetFactor(double(DBGBITMAPWIDTH) / windowWidth);
1758 cSubtitleRegion *region = page->GetRegionById(0, true);
1759 region->ParsePgs(bs);
1760 break;
1761 }
1762 case PGS_WINDOW_SEGMENT: {
1763 bs.SkipBits(16);
1764 int regionHorizontalAddress = bs.GetBits(16);
1765 int regionVerticalAddress = bs.GetBits(16);
1766 int regionWidth = bs.GetBits(16);
1767 int regionHeight = bs.GetBits(16);
1768 cSubtitleRegion *region = page->GetRegionById(0, true);
1769 region->SetDimensions(regionWidth, regionHeight);
1770 page->AddRegionRef(new cSubtitleRegionRef(0, regionHorizontalAddress, regionVerticalAddress));
1771 dbgsegments("PGS_WINDOW_SEGMENT<br>\n");
1772 break;
1773 }
1774 case PGS_PALETTE_SEGMENT: {
1775 dbgsegments("PGS_PALETTE_SEGMENT<br>\n");
1776 cSubtitleClut *clut = page->GetClutById(bs.GetBits(8), true);
1777 clut->ParsePgs(bs);
1778 break;
1779 }
1780 case PGS_OBJECT_SEGMENT: {
1781 dbgsegments("PGS_OBJECT_SEGMENT<br>\n");
1782 cSubtitleObject *object = page->GetObjectById(bs.GetBits(16), true);
1783 object->ParsePgs(bs);
1784 break;
1785 }
1786 case PGS_DISPLAY_SEGMENT: {
1787 dbgsegments("PGS_DISPLAY_SEGMENT<br>\n");
1788 FinishPage(page);
1789 page->SetPending(false);
1790 break;
1791 }
1792 default:
1793 dbgsegments("*** unknown segment type: %02X<br>\n", segmentType);
1794 return -1;
1795 }
1796 return bs.Length() / 8;
1797 }
1798 return -1;
1799}
1800
1802{
1803 if (!AssertOsd())
1804 return;
1805 int NumAreas;
1806 tArea *Areas = Page->GetAreas(NumAreas);
1807 tArea AreaCombined = Page->CombineAreas(NumAreas, Areas);
1808 cDvbSubtitleBitmaps *After = NULL;
1809 for (cDvbSubtitleBitmaps *sb = bitmaps->Last(); sb; sb = bitmaps->Prev(sb)) {
1810 int64_t Delta = PtsDeltaMs(sb->Pts(), Page->Pts());
1811 if (Delta == 0)
1812 return; // we already have this one
1813 if (Delta < 0) {
1814 After = sb;
1815 break;
1816 }
1817 }
1818 tArea AreaOsd = Page->ScaleArea(AreaCombined, osdFactorX, osdFactorY);
1819 int Bpp = 8;
1820 bool Reduced = false;
1821 if (NumAreas) {
1822 while (osd->CanHandleAreas(&AreaOsd, 1) != oeOk) {
1823 dbgoutput("CanHandleAreas: %d<br>\n", osd->CanHandleAreas(&AreaOsd, 1));
1824 int HalfBpp = Bpp / 2;
1825 if (HalfBpp >= 2) {
1826 if (AreaOsd.bpp >= Bpp) {
1827 AreaOsd.bpp = HalfBpp;
1828 Reduced = true;
1829 }
1830 Bpp = HalfBpp;
1831 }
1832 else
1833 return; // unable to draw bitmaps
1834 }
1835 }
1836 cDvbSubtitleBitmaps *Bitmaps = new cDvbSubtitleBitmaps(Page->PageState(), Page->Pts(), Page->PageTimeout(), Areas, NumAreas, osdFactorX, osdFactorY, AreaCombined, AreaOsd);
1837 if (After)
1838 bitmaps->Add(Bitmaps, After);
1839 else
1840 bitmaps->Ins(Bitmaps);
1841 for (int i = 0; i < NumAreas; i++) {
1842 if (cSubtitleRegionRef *srr = Page->GetRegionRefByIndex(i)) {
1843 if (cSubtitleRegion *sr = Page->GetRegionById(srr->RegionId())) {
1844 if (cSubtitleClut *clut = Page->GetClutById(sr->ClutId())) {
1845 cBitmap *bm = new cBitmap(sr->RegionWidth(), sr->RegionHeight(), sr->RegionDepth());
1846 bm->Replace(*clut->GetPalette(sr->RegionDepth()));
1847 sr->Render(bm, Page->Objects());
1848 if (Reduced) {
1849 if (sr->RegionDepth() != Areas[i].bpp) {
1850 if (sr->RegionLevelOfCompatibility() <= Areas[i].bpp) {
1851 //TODO this is untested - didn't have any such subtitle stream
1852 cSubtitleClut *Clut = Page->GetClutById(sr->ClutId());
1853 dbgregions("reduce region %d bpp %d level %d area bpp %d<br>\n", sr->RegionId(), sr->RegionDepth(), sr->RegionLevelOfCompatibility(), Areas[i].bpp);
1854 bm->ReduceBpp(*Clut->GetPalette(sr->RegionDepth()));
1855 }
1856 else {
1857 dbgregions("condense region %d bpp %d level %d area bpp %d<br>\n", sr->RegionId(), sr->RegionDepth(), sr->RegionLevelOfCompatibility(), Areas[i].bpp);
1858 bm->ShrinkBpp(Areas[i].bpp);
1859 }
1860 }
1861 }
1862 bm->SetOffset(srr->RegionHorizontalAddress(), srr->RegionVerticalAddress());
1863 Bitmaps->AddBitmap(bm);
1864 }
1865 }
1866 }
1867 }
1868 if (DebugPages)
1869 Bitmaps->DbgDump(windowWidth, windowHeight);
1870}
void SkipBit(void)
Definition tools.h:396
int Index(void) const
Definition tools.h:400
void WordAlign(void)
Definition tools.c:1499
bool SetLength(int Length)
Definition tools.c:1506
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:1484
void ByteAlign(void)
Definition tools.c:1492
int GetBit(void)
Definition tools.c:1475
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:1029
static void SleepMs(int TimeoutMs)
Creates a cCondWait object and uses it to sleep for TimeoutMs milliseconds, immediately giving up the...
Definition thread.c:73
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:1292
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)
virtual ~cDvbSubtitleBitmaps() override
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)
virtual void Action(void) override
A derived cThread class must implement the code it wants to execute as a separate thread in this func...
void SetVisible(bool Visible)
cList< cDvbSubtitleBitmaps > * bitmaps
Definition dvbsubtitle.h:44
void FinishPage(cDvbSubtitlePage *Page)
int ExtractPgsSegment(const uchar *Data, int Length, int64_t Pts)
virtual ~cDvbSubtitleConverter() override
int ExtractSegment(const uchar *Data, int Length, int64_t Pts)
cDvbSubtitleAssembler * dvbSubtitleAssembler
Definition dvbsubtitle.h:26
cList< cDvbSubtitlePage > * pages
Definition dvbsubtitle.h:43
cDvbSubtitlePage * GetPageById(int PageId, bool New=false)
static void SetupChanged(void)
int ConvertFragments(const uchar *Data, int Length)
cDvbSubtitleBitmaps * current
Definition dvbsubtitle.h:45
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).
void Add(cListObject *Object, cListObject *After=NULL)
Definition tools.c:2177
cListObject(const cListObject &ListObject)
Definition tools.h:534
int Index(void) const
Definition tools.c:2097
Definition tools.h:631
const cSubtitleObject * First(void) const
Definition tools.h:643
cList(const char *NeedsLocking=NULL)
Definition tools.h:633
const cSubtitleObject * Next(const cSubtitleObject *Object) const
Definition tools.h:650
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
int Bpp(void) const
Definition osd.h:111
static cString sprintf(const char *fmt,...) __attribute__((format(printf
Definition tools.c:1195
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:305
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
cThread(const char *Description=NULL, bool LowPriority=false)
Creates a new thread.
Definition thread.c:239
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:355
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 SUBTITLE_RETENTION
#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
static int PtsDeltaMs(int64_t a, int64_t b)
#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 TEMPSUBTITLETAIL
#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 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:1366
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