vdr  2.7.6
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.
37 static bool DebugNormal = false; // shows pages, regions and objects
38 static bool DebugVerbose = false; // shows everything
45 static bool DebugPixel = DebugVerbose;
46 static bool DebugCluts = DebugVerbose;
47 static bool DebugOutput = DebugVerbose;
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 
67 private:
69  int imgCnt;
70  int64_t firstPts;
71  bool newFile;
72  double factor;
73 public:
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 
92 cString 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 
124 void 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 
143 class cSubtitleClut : public cListObject {
144 private:
145  int clutId;
150  tColor yuv2rgb(int Y, int Cb, int Cr);
151  void SetColor(int Bpp, int Index, tColor Color);
152 public:
153  cSubtitleClut(int ClutId);
154  void Parse(cBitStream &bs);
155  void ParsePgs(cBitStream &bs);
156  int ClutId(void) { return clutId; }
157  int ClutVersionNumber(void) { return clutVersionNumber; }
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;
168  clutVersionNumber = -1;
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 
302 tColor 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 
318 void 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 
341 class cSubtitleObject : public cListObject {
342 private:
343  int objectId;
349  int topIndex;
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);
361 public:
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 {
375  objectId = ObjectId;
376  objectVersionNumber = -1;
377  objectCodingMethod = -1;
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;
403  objectCodingMethod = bs.GetBits(2);
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;
449  objectCodingMethod = 0;
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 
466 void 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 
494 void 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 
563 void 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 
571 bool 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 
611 bool 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 
656 bool 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 
681 bool 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 
703 void 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 
729 class cSubtitleObjects : public cList<cSubtitleObject> {
730 public:
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 
750 protected:
751  int objectId;
758 public:
759  cSubtitleObjectRef(void);
761  int ObjectId(void) { return objectId; }
762  int ObjectType(void) { return objectType; }
768  };
769 
771 {
772  objectId = 0;
773  objectType = 0;
774  objectProviderFlag = 0;
779 }
780 
782 {
783  objectId = bs.GetBits(16);
784  objectType = bs.GetBits(2);
785  objectProviderFlag = 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 
803 private:
804  int windowId;
806  int cropX;
807  int cropY;
808  int cropW;
809  int cropH;
810 public:
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 
834 class cSubtitleRegion : public cListObject {
835 private:
836  int regionId;
843  int clutId;
848 public:
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 {
866  regionId = RegionId;
867  regionVersionNumber = -1;
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 
916 void 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 
943 private:
944  int regionId;
947 public:
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 
974 private:
975  int pageId;
979  int64_t pts;
980  bool pending;
985 public:
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; }
991  int PageVersionNumber(void) { return pageVersionNumber; }
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); }
1004  void SetPending(bool Pending) { pending = Pending; }
1005  };
1006 
1008 {
1009  pageId = PageId;
1010  pageTimeout = 0;
1011  pageVersionNumber = -1;
1012  pageState = -1;
1013  pts = -1;
1014  pending = false;
1015 }
1016 
1017 void cDvbSubtitlePage::Parse(int64_t Pts, cBitStream &bs)
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 
1106 tArea 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 
1124 tArea 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 
1171 private:
1173  int length;
1174  int pos;
1175  int size;
1176  bool Realloc(int Size);
1177 public:
1178  cDvbSubtitleAssembler(void);
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 
1223 unsigned 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 
1236 void 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 
1247 private:
1248  int state;
1249  int64_t pts;
1250  int timeout;
1255  double osdFactorX;
1256  double osdFactorY;
1258 public:
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 
1270 cDvbSubtitleBitmaps::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 
1321 void 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;
1366  displayHeight = windowHeight = 576;
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 {
1392  LOCK_THREAD;
1393  visible = Visible;
1394  if (!visible)
1395  DELETENULL(osd);
1396  endVisible = -1;
1397 }
1398 
1400 {
1401  LOCK_THREAD;
1402  if (!visible) {
1404  visible = true;
1405  }
1406 }
1407 
1409 {
1410  setupLevel++;
1411 }
1412 
1414 {
1415  dbgconverter("converter reset -----------------------<br>\n");
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)
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 
1472 int 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 
1510 static 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) {
1523  LOCK_THREAD;
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 {
1595  LOCK_THREAD;
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 
1616 int 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;
1623  LOCK_THREAD;
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) {
1629  case PAGE_COMPOSITION_SEGMENT: {
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);
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  }
1645  case CLUT_DEFINITION_SEGMENT: {
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 
1736 int 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;
1744  LOCK_THREAD;
1745  cDvbSubtitlePage *page = GetPageById(0, true);
1746  switch (segmentType) {
1747  case PGS_PRESENTATION_SEGMENT: {
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);
1754  displayHeight = windowHeight = bs.GetBits(16);
1755  bs.SkipBits(8);
1756  page->ParsePgs(Pts, bs);
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
const uint8_t * GetData(void) const
Definition: tools.h:401
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
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
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
static cDevice * PrimaryDevice(void)
Returns the primary device.
Definition: device.h:148
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()
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
virtual ~cDvbSubtitleBitmaps() override
Definition: dvbsubtitle.c:1283
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:1472
virtual void Action(void) override
A derived cThread class must implement the code it wants to execute as a separate thread in this func...
Definition: dvbsubtitle.c:1517
void SetTempVisible(void)
Definition: dvbsubtitle.c:1399
void SetVisible(bool Visible)
Definition: dvbsubtitle.c:1390
cList< cDvbSubtitleBitmaps > * bitmaps
Definition: dvbsubtitle.h:44
void FinishPage(cDvbSubtitlePage *Page)
Definition: dvbsubtitle.c:1801
int ExtractPgsSegment(const uchar *Data, int Length, int64_t Pts)
Definition: dvbsubtitle.c:1736
virtual ~cDvbSubtitleConverter() override
Definition: dvbsubtitle.c:1381
int ExtractSegment(const uchar *Data, int Length, int64_t Pts)
Definition: dvbsubtitle.c:1616
cDvbSubtitleAssembler * dvbSubtitleAssembler
Definition: dvbsubtitle.h:26
cList< cDvbSubtitlePage > * pages
Definition: dvbsubtitle.h:43
static int setupLevel
Definition: dvbsubtitle.h:25
cDvbSubtitlePage * GetPageById(int PageId, bool New=false)
Definition: dvbsubtitle.c:1603
static void SetupChanged(void)
Definition: dvbsubtitle.c:1408
int ConvertFragments(const uchar *Data, int Length)
Definition: dvbsubtitle.c:1426
cDvbSubtitleBitmaps * current
Definition: dvbsubtitle.h:45
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
cSubtitleObjects * Objects(void)
Definition: dvbsubtitle.c:995
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
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
cSubtitleRegionRef * GetRegionRefByIndex(int RegionRefIndex)
Definition: dvbsubtitle.c:1002
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:429
virtual int Height(void) const =0
Returns the height of this font in pixel (all characters have the same height).
virtual void Clear(void)
Definition: tools.c:2254
void Ins(cListObject *Object, cListObject *Before=NULL)
Definition: tools.c:2193
void Del(cListObject *Object, bool DeleteObject=true)
Definition: tools.c:2209
int Count(void) const
Definition: tools.h:627
void Add(cListObject *Object, cListObject *After=NULL)
Definition: tools.c:2177
int Index(void) const
Definition: tools.c:2097
Definition: tools.h:631
const cSubtitleObject * Next(const cSubtitleObject *Object) const
< Returns the element immediately before Object in this list, or NULL if Object is the first element ...
Definition: tools.h:650
const T * Last(void) const
Returns the last element in this list, or NULL if the list is empty.
Definition: tools.h:645
const cSubtitleObject * First(void) const
Returns the first element in this list, or NULL if the list is empty.
Definition: tools.h:643
const T * Get(int Index) const
Returns the list element at the given Index, or NULL if no such element exists.
Definition: tools.h:640
const T * Prev(const T *Object) const
Definition: tools.h:647
Definition: thread.h:67
static cOsd * NewOsd(int Left, int Top, uint Level=OSD_LEVEL_DEFAULT)
Returns a pointer to a newly created cOsd object, which will be located at the given coordinates.
Definition: osd.c:2290
The cOsd class is the interface to the "On Screen Display".
Definition: osd.h:753
virtual eOsdError SetAreas(const tArea *Areas, int NumAreas)
Sets the sub-areas to the given areas.
Definition: osd.c:2092
virtual eOsdError CanHandleAreas(const tArea *Areas, int NumAreas)
Checks whether the OSD can display the given set of sub-areas.
Definition: osd.c:2070
virtual void DrawScaledBitmap(int x, int y, const cBitmap &Bitmap, double FactorX, double FactorY, bool AntiAlias=false)
Sets the pixels in the OSD with the data from the given Bitmap, putting the upper left corner of the ...
Definition: osd.c:2216
virtual void Flush(void)
Actually commits all data to the OSD hardware.
Definition: osd.c:2266
Definition: osd.h:88
tColor Color(int Index) const
Returns the color at the given Index.
Definition: osd.h:119
void Replace(const cPalette &Palette)
Replaces the colors of this palette with the colors from the given palette.
Definition: osd.c:208
void SetColor(int Index, tColor Color)
Sets the palette entry at Index to Color.
Definition: osd.c:172
int Bpp(void) const
Definition: osd.h:111
int SubtitleFgTransparency
Definition: config.h:304
int AntiAlias
Definition: config.h:344
int FontOsdSize
Definition: config.h:351
int SubtitleOffset
Definition: config.h:303
int SubtitleBgTransparency
Definition: config.h:304
char FontOsd[MAXFONTNAME]
Definition: config.h:345
Definition: tools.h:178
static cString sprintf(const char *fmt,...) __attribute__((format(printf
Definition: tools.c:1195
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
Definition: thread.h:79
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
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
int Size(void) const
Definition: tools.h:754
virtual void Append(T Data)
Definition: tools.h:774
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:1508
#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
Definition: dvbsubtitle.c:1354
#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)
Definition: dvbsubtitle.c:1510
#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 TEMPSUBTITLETAIL
Definition: dvbsubtitle.c:1515
#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:29
uint8_t tIndex
Definition: font.h:31
const char * getCharacterTable(const unsigned char *&buffer, int &length, bool *isSingleByte)
Definition: si.c:364
#define OSD_LEVEL_SUBTITLES
Definition: osd.h:22
@ oeOk
Definition: osd.h:44
tColor ArgbToColor(uint8_t A, uint8_t R, uint8_t G, uint8_t B)
Definition: osd.h:58
int PesPayloadOffset(const uchar *p)
Definition: remux.h:178
bool PesHasPts(const uchar *p)
Definition: remux.h:183
int64_t PesGetPts(const uchar *p)
Definition: remux.h:193
Definition: osd.h:298
int Width(void) const
Definition: osd.h:301
int bpp
Definition: osd.h:300
int x2
Definition: osd.h:299
int y1
Definition: osd.h:299
int x1
Definition: osd.h:299
int Height(void) const
Definition: osd.h:302
int y2
Definition: osd.h:299
#define LOCK_THREAD
Definition: thread.h:167
uchar * RgbToJpeg(uchar *Mem, int Width, int Height, int &Size, int Quality)
Converts the given Memory to a JPEG image and returns a pointer to the resulting image.
Definition: tools.c: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