vdr  2.6.9
dvbsubtitle.c
Go to the documentation of this file.
1 /*
2  * dvbsubtitle.c: DVB subtitles
3  *
4  * See the main source file 'vdr.c' for copyright information and
5  * how to reach the author.
6  *
7  * Original author: Marco Schluessler <marco@lordzodiac.de>
8  * With some input from the "subtitles plugin" by Pekka Virtanen <pekka.virtanen@sci.fi>
9  *
10  * $Id: dvbsubtitle.c 5.2 2022/12/06 16:57:01 kls Exp $
11  */
12 
13 #include "dvbsubtitle.h"
14 #define __STDC_FORMAT_MACROS // Required for format specifiers
15 #include <inttypes.h>
16 #include "device.h"
17 #include "libsi/si.h"
18 
19 #define PAGE_COMPOSITION_SEGMENT 0x10
20 #define REGION_COMPOSITION_SEGMENT 0x11
21 #define CLUT_DEFINITION_SEGMENT 0x12
22 #define OBJECT_DATA_SEGMENT 0x13
23 #define DISPLAY_DEFINITION_SEGMENT 0x14
24 #define DISPARITY_SIGNALING_SEGMENT 0x15 // DVB BlueBook A156
25 #define END_OF_DISPLAY_SET_SEGMENT 0x80
26 #define STUFFING_SEGMENT 0xFF
27 
28 #define PGS_PALETTE_SEGMENT 0x14
29 #define PGS_OBJECT_SEGMENT 0x15
30 #define PGS_PRESENTATION_SEGMENT 0x16
31 #define PGS_WINDOW_SEGMENT 0x17
32 #define PGS_DISPLAY_SEGMENT 0x80
33 
34 // Set these to 'true' for debug output, which is written into the file dbg-log.htm
35 // in the current working directory. The HTML file shows the actual bitmaps (dbg-nnn.jpg)
36 // used to display the subtitles.
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);
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 
1355 
1357 :cThread("subtitle converter")
1358 {
1360  osd = NULL;
1361  frozen = false;
1362  ddsVersionNumber = -1;
1363  displayWidth = windowWidth = 720;
1364  displayHeight = windowHeight = 576;
1369  SD.Reset();
1370  Start();
1371 }
1372 
1374 {
1375  Cancel(3);
1376  delete dvbSubtitleAssembler;
1377  delete osd;
1378  delete bitmaps;
1379  delete pages;
1380 }
1381 
1383 {
1384  setupLevel++;
1385 }
1386 
1388 {
1389  dbgconverter("converter reset -----------------------<br>\n");
1391  Lock();
1392  pages->Clear();
1393  bitmaps->Clear();
1394  DELETENULL(osd);
1395  frozen = false;
1396  ddsVersionNumber = -1;
1397  displayWidth = windowWidth = 720;
1398  displayHeight = windowHeight = 576;
1401  Unlock();
1402 }
1403 
1405 {
1406  if (Data && Length > 8) {
1407  int PayloadOffset = PesPayloadOffset(Data);
1408  int SubstreamHeaderLength = 4;
1409  bool ResetSubtitleAssembler = Data[PayloadOffset + 3] == 0x00;
1410 
1411  // Compatibility mode for old subtitles plugin:
1412  if ((Data[7] & 0x01) && (Data[PayloadOffset - 3] & 0x81) == 0x01 && Data[PayloadOffset - 2] == 0x81) {
1413  PayloadOffset--;
1414  SubstreamHeaderLength = 1;
1415  ResetSubtitleAssembler = Data[8] >= 5;
1416  }
1417 
1418  if (Length > PayloadOffset + SubstreamHeaderLength) {
1419  int64_t pts = PesHasPts(Data) ? PesGetPts(Data) : -1;
1420  if (pts >= 0)
1421  dbgconverter("converter PTS: %" PRId64 "<br>\n", pts);
1422  const uchar *data = Data + PayloadOffset + SubstreamHeaderLength; // skip substream header
1423  int length = Length - PayloadOffset - SubstreamHeaderLength; // skip substream header
1424  if (ResetSubtitleAssembler)
1426 
1427  if (length > 3) {
1428  if (data[0] == 0x20 && data[1] == 0x00 && data[2] == 0x0F)
1429  dvbSubtitleAssembler->Put(data + 2, length - 2);
1430  else
1431  dvbSubtitleAssembler->Put(data, length);
1432 
1433  int Count;
1434  while (true) {
1435  unsigned char *b = dvbSubtitleAssembler->Get(Count);
1436  if (b && b[0] == 0x0F) {
1437  if (ExtractSegment(b, Count, pts) == -1)
1438  break;
1439  }
1440  else
1441  break;
1442  }
1443  }
1444  }
1445  return Length;
1446  }
1447  return 0;
1448 }
1449 
1450 int cDvbSubtitleConverter::Convert(const uchar *Data, int Length)
1451 {
1452  if (Data && Length > 8) {
1453  int PayloadOffset = PesPayloadOffset(Data);
1454  if (Length > PayloadOffset) {
1455  int64_t pts = PesHasPts(Data) ? PesGetPts(Data) : -1;
1456  if (pts >= 0)
1457  dbgconverter("converter PTS: %" PRId64 "<br>\n", pts);
1458  const uchar *data = Data + PayloadOffset;
1459  int length = Length - PayloadOffset;
1460  if (length > 0) {
1461  if (length > 2 && data[0] == 0x20 && data[1] == 0x00 && data[2] == 0x0F) {
1462  data += 2;
1463  length -= 2;
1464  }
1465  const uchar *b = data;
1466  while (length > 0) {
1467  if (b[0] == STUFFING_SEGMENT)
1468  break;
1469  int n;
1470  if (b[0] == 0x0F)
1471  n = ExtractSegment(b, length, pts);
1472  else
1473  n = ExtractPgsSegment(b, length, pts);
1474  if (n < 0)
1475  break;
1476  b += n;
1477  length -= n;
1478  }
1479  }
1480  }
1481  return Length;
1482  }
1483  return 0;
1484 }
1485 
1486 #define LimitTo32Bit(n) ((n) & 0x00000000FFFFFFFFL)
1487 
1489 {
1490  int LastSetupLevel = setupLevel;
1491  cTimeMs Timeout;
1492  while (Running()) {
1493  int WaitMs = 100;
1494  if (!frozen) {
1495  LOCK_THREAD;
1496  if (osd) {
1497  int NewSetupLevel = setupLevel;
1498  if (Timeout.TimedOut() || LastSetupLevel != NewSetupLevel) {
1499  dbgoutput("closing osd<br>\n");
1500  DELETENULL(osd);
1501  }
1502  LastSetupLevel = NewSetupLevel;
1503  }
1504  for (cDvbSubtitleBitmaps *sb = bitmaps->First(); sb; sb = bitmaps->Next(sb)) {
1505  // Calculate the Delta between the STC (the current timestamp of the video)
1506  // and the bitmap's PTS (the timestamp when the bitmap shall be presented).
1507  // A negative Delta means that the bitmap will be presented in the future:
1508  int64_t STC = cDevice::PrimaryDevice()->GetSTC();
1509  int64_t Delta = LimitTo32Bit(STC) - LimitTo32Bit(sb->Pts()); // some devices only deliver 32 bits
1510  if (Delta > (int64_t(1) << 31))
1511  Delta -= (int64_t(1) << 32);
1512  else if (Delta < -((int64_t(1) << 31) - 1))
1513  Delta += (int64_t(1) << 32);
1514  Delta /= 90; // STC and PTS are in 1/90000s
1515  if (Delta >= 0) { // found a bitmap that shall be displayed...
1516  if (Delta < sb->Timeout() * 1000) { // ...and has not timed out yet
1517  if (!sb->HasBitmaps()) {
1518  Timeout.Set();
1519  WaitMs = 0;
1520  }
1521  else if (AssertOsd()) {
1522  dbgoutput("showing bitmap #%d of %d<br>\n", sb->Index() + 1, bitmaps->Count());
1523  sb->Draw(osd);
1524  Timeout.Set(sb->Timeout() * 1000);
1525  dbgconverter("PTS: %" PRId64 " STC: %" PRId64 " (%" PRId64 ") timeout: %d<br>\n", sb->Pts(), STC, Delta, sb->Timeout());
1526  }
1527  }
1528  else
1529  WaitMs = 0; // bitmap already timed out, so try next one immediately
1530  dbgoutput("deleting bitmap #%d of %d<br>\n", sb->Index() + 1, bitmaps->Count());
1531  bitmaps->Del(sb);
1532  break;
1533  }
1534  }
1535  }
1536  cCondWait::SleepMs(WaitMs);
1537  }
1538 }
1539 
1541 {
1542  int OsdWidth, OsdHeight;
1543  double OsdAspect;
1544  int VideoWidth, VideoHeight;
1545  double VideoAspect;
1546  cDevice::PrimaryDevice()->GetOsdSize(OsdWidth, OsdHeight, OsdAspect);
1547  cDevice::PrimaryDevice()->GetVideoSize(VideoWidth, VideoHeight, VideoAspect);
1548  if (OsdWidth == displayWidth && OsdHeight == displayHeight) {
1549  osdFactorX = osdFactorY = 1.0;
1550  osdDeltaX = osdDeltaY = 0;
1551  }
1552  else {
1553  osdFactorX = osdFactorY = min(double(OsdWidth) / displayWidth, double(OsdHeight) / displayHeight);
1554  osdDeltaX = (OsdWidth - displayWidth * osdFactorX) / 2;
1555  osdDeltaY = (OsdHeight - displayHeight * osdFactorY) / 2;
1556  }
1557 }
1558 
1560 {
1561  LOCK_THREAD;
1562  if (!osd) {
1563  SetOsdData();
1565  }
1566  return osd != NULL;
1567 }
1568 
1570 {
1571  for (cDvbSubtitlePage *sp = pages->First(); sp; sp = pages->Next(sp)) {
1572  if (sp->PageId() == PageId)
1573  return sp;
1574  }
1575  if (!New)
1576  return NULL;
1577  cDvbSubtitlePage *Page = new cDvbSubtitlePage(PageId);
1578  pages->Add(Page);
1579  return Page;
1580 }
1581 
1582 int cDvbSubtitleConverter::ExtractSegment(const uchar *Data, int Length, int64_t Pts)
1583 {
1584  cBitStream bs(Data, Length * 8);
1585  if (Length > 5 && bs.GetBits(8) == 0x0F) { // sync byte
1586  int segmentType = bs.GetBits(8);
1587  if (segmentType == STUFFING_SEGMENT)
1588  return -1;
1589  LOCK_THREAD;
1590  cDvbSubtitlePage *page = GetPageById(bs.GetBits(16), true);
1591  int segmentLength = bs.GetBits(16);
1592  if (!bs.SetLength(bs.Index() + segmentLength * 8))
1593  return -1;
1594  switch (segmentType) {
1595  case PAGE_COMPOSITION_SEGMENT: {
1596  if (page->Pending()) {
1597  dbgsegments("END_OF_DISPLAY_SET_SEGMENT (simulated)<br>\n");
1598  FinishPage(page);
1599  }
1600  dbgsegments("PAGE_COMPOSITION_SEGMENT<br>\n");
1601  page->Parse(Pts, bs);
1603  break;
1604  }
1606  dbgsegments("REGION_COMPOSITION_SEGMENT<br>\n");
1607  cSubtitleRegion *region = page->GetRegionById(bs.GetBits(8), true);
1608  region->Parse(bs);
1609  break;
1610  }
1611  case CLUT_DEFINITION_SEGMENT: {
1612  dbgsegments("CLUT_DEFINITION_SEGMENT<br>\n");
1613  cSubtitleClut *clut = page->GetClutById(bs.GetBits(8), true);
1614  clut->Parse(bs);
1615  break;
1616  }
1617  case OBJECT_DATA_SEGMENT: {
1618  dbgsegments("OBJECT_DATA_SEGMENT<br>\n");
1619  cSubtitleObject *object = page->GetObjectById(bs.GetBits(16), true);
1620  object->Parse(bs);
1621  break;
1622  }
1624  dbgsegments("DISPLAY_DEFINITION_SEGMENT<br>\n");
1625  int version = bs.GetBits(4);
1626 #ifndef FIX_SUBTITLE_VERSION_BROADCASTER_STUPIDITY
1627  if (version == ddsVersionNumber)
1628  break; // no update
1629 #endif
1630  bool displayWindowFlag = bs.GetBit();
1633  bs.SkipBits(3); // reserved
1634  displayWidth = windowWidth = bs.GetBits(16) + 1;
1635  displayHeight = windowHeight = bs.GetBits(16) + 1;
1636  if (displayWindowFlag) {
1637  windowHorizontalOffset = bs.GetBits(16); // displayWindowHorizontalPositionMinimum
1638  windowWidth = bs.GetBits(16) - windowHorizontalOffset + 1; // displayWindowHorizontalPositionMaximum
1639  windowVerticalOffset = bs.GetBits(16); // displayWindowVerticalPositionMinimum
1640  windowHeight = bs.GetBits(16) - windowVerticalOffset + 1; // displayWindowVerticalPositionMaximum
1641  }
1642  SetOsdData();
1643  ddsVersionNumber = version;
1644  dbgdisplay("<b>display</b> version %d flag %d width %d height %d ofshor %d ofsver %d<br>\n", ddsVersionNumber, displayWindowFlag, windowWidth, windowHeight, windowHorizontalOffset, windowVerticalOffset);
1645  break;
1646  }
1648  dbgsegments("DISPARITY_SIGNALING_SEGMENT<br>\n");
1649  bs.SkipBits(4); // dss_version_number
1650  bool disparity_shift_update_sequence_page_flag = bs.GetBit();
1651  bs.SkipBits(3); // reserved
1652  bs.SkipBits(8); // page_default_disparity_shift
1653  if (disparity_shift_update_sequence_page_flag) {
1654  bs.SkipBits(8); // disparity_shift_update_sequence_length
1655  bs.SkipBits(24); // interval_duration[23..0]
1656  int division_period_count = bs.GetBits(8);
1657  for (int i = 0; i < division_period_count; ++i) {
1658  bs.SkipBits(8); // interval_count
1659  bs.SkipBits(8); // disparity_shift_update_integer_part
1660  }
1661  }
1662  while (!bs.IsEOF()) {
1663  bs.SkipBits(8); // region_id
1664  bool disparity_shift_update_sequence_region_flag = bs.GetBit();
1665  bs.SkipBits(5); // reserved
1666  int number_of_subregions_minus_1 = bs.GetBits(2);
1667  for (int i = 0; i <= number_of_subregions_minus_1; ++i) {
1668  if (number_of_subregions_minus_1 > 0) {
1669  bs.SkipBits(16); // subregion_horizontal_position
1670  bs.SkipBits(16); // subregion_width
1671  }
1672  bs.SkipBits(8); // subregion_disparity_shift_integer_part
1673  bs.SkipBits(4); // subregion_disparity_shift_fractional_part
1674  bs.SkipBits(4); // reserved
1675  if (disparity_shift_update_sequence_region_flag) {
1676  bs.SkipBits(8); // disparity_shift_update_sequence_length
1677  bs.SkipBits(24); // interval_duration[23..0]
1678  int division_period_count = bs.GetBits(8);
1679  for (int i = 0; i < division_period_count; ++i) {
1680  bs.SkipBits(8); // interval_count
1681  bs.SkipBits(8); // disparity_shift_update_integer_part
1682  }
1683  }
1684  }
1685  }
1686  break;
1687  }
1689  dbgsegments("END_OF_DISPLAY_SET_SEGMENT<br>\n");
1690  FinishPage(page);
1691  page->SetPending(false);
1692  break;
1693  }
1694  default:
1695  dbgsegments("*** unknown segment type: %02X<br>\n", segmentType);
1696  }
1697  return bs.Length() / 8;
1698  }
1699  return -1;
1700 }
1701 
1702 int cDvbSubtitleConverter::ExtractPgsSegment(const uchar *Data, int Length, int64_t Pts)
1703 {
1704  cBitStream bs(Data, Length * 8);
1705  if (Length >= 3) {
1706  int segmentType = bs.GetBits(8);
1707  int segmentLength = bs.GetBits(16);
1708  if (!bs.SetLength(bs.Index() + segmentLength * 8))
1709  return -1;
1710  LOCK_THREAD;
1711  cDvbSubtitlePage *page = GetPageById(0, true);
1712  switch (segmentType) {
1713  case PGS_PRESENTATION_SEGMENT: {
1714  if (page->Pending()) {
1715  dbgsegments("PGS_DISPLAY_SEGMENT (simulated)<br>\n");
1716  FinishPage(page);
1717  }
1718  dbgsegments("PGS_PRESENTATION_SEGMENT<br>\n");
1719  displayWidth = windowWidth = bs.GetBits(16);
1720  displayHeight = windowHeight = bs.GetBits(16);
1721  bs.SkipBits(8);
1722  page->ParsePgs(Pts, bs);
1724  cSubtitleRegion *region = page->GetRegionById(0, true);
1725  region->ParsePgs(bs);
1726  break;
1727  }
1728  case PGS_WINDOW_SEGMENT: {
1729  bs.SkipBits(16);
1730  int regionHorizontalAddress = bs.GetBits(16);
1731  int regionVerticalAddress = bs.GetBits(16);
1732  int regionWidth = bs.GetBits(16);
1733  int regionHeight = bs.GetBits(16);
1734  cSubtitleRegion *region = page->GetRegionById(0, true);
1735  region->SetDimensions(regionWidth, regionHeight);
1736  page->AddRegionRef(new cSubtitleRegionRef(0, regionHorizontalAddress, regionVerticalAddress));
1737  dbgsegments("PGS_WINDOW_SEGMENT<br>\n");
1738  break;
1739  }
1740  case PGS_PALETTE_SEGMENT: {
1741  dbgsegments("PGS_PALETTE_SEGMENT<br>\n");
1742  cSubtitleClut *clut = page->GetClutById(bs.GetBits(8), true);
1743  clut->ParsePgs(bs);
1744  break;
1745  }
1746  case PGS_OBJECT_SEGMENT: {
1747  dbgsegments("PGS_OBJECT_SEGMENT<br>\n");
1748  cSubtitleObject *object = page->GetObjectById(bs.GetBits(16), true);
1749  object->ParsePgs(bs);
1750  break;
1751  }
1752  case PGS_DISPLAY_SEGMENT: {
1753  dbgsegments("PGS_DISPLAY_SEGMENT<br>\n");
1754  FinishPage(page);
1755  page->SetPending(false);
1756  break;
1757  }
1758  default:
1759  dbgsegments("*** unknown segment type: %02X<br>\n", segmentType);
1760  return -1;
1761  }
1762  return bs.Length() / 8;
1763  }
1764  return -1;
1765 }
1766 
1768 {
1769  if (!AssertOsd())
1770  return;
1771  int NumAreas;
1772  tArea *Areas = Page->GetAreas(NumAreas);
1773  if (!Areas)
1774  return;
1775  tArea AreaCombined = Page->CombineAreas(NumAreas, Areas);
1776  tArea AreaOsd = Page->ScaleArea(AreaCombined, osdFactorX, osdFactorY);
1777  int Bpp = 8;
1778  bool Reduced = false;
1779  if (osd) {
1780  while (osd->CanHandleAreas(&AreaOsd, 1) != oeOk) {
1781  dbgoutput("CanHandleAreas: %d<br>\n", osd->CanHandleAreas(&AreaOsd, 1));
1782  int HalfBpp = Bpp / 2;
1783  if (HalfBpp >= 2) {
1784  if (AreaOsd.bpp >= Bpp) {
1785  AreaOsd.bpp = HalfBpp;
1786  Reduced = true;
1787  }
1788  Bpp = HalfBpp;
1789  }
1790  else
1791  return; // unable to draw bitmaps
1792  }
1793  }
1794  cDvbSubtitleBitmaps *Bitmaps = new cDvbSubtitleBitmaps(Page->PageState(), Page->Pts(), Page->PageTimeout(), Areas, NumAreas, osdFactorX, osdFactorY, AreaCombined, AreaOsd);
1795  bitmaps->Add(Bitmaps);
1796  for (int i = 0; i < NumAreas; i++) {
1797  if (cSubtitleRegionRef *srr = Page->GetRegionRefByIndex(i)) {
1798  if (cSubtitleRegion *sr = Page->GetRegionById(srr->RegionId())) {
1799  if (cSubtitleClut *clut = Page->GetClutById(sr->ClutId())) {
1800  cBitmap *bm = new cBitmap(sr->RegionWidth(), sr->RegionHeight(), sr->RegionDepth());
1801  bm->Replace(*clut->GetPalette(sr->RegionDepth()));
1802  sr->Render(bm, Page->Objects());
1803  if (Reduced) {
1804  if (sr->RegionDepth() != Areas[i].bpp) {
1805  if (sr->RegionLevelOfCompatibility() <= Areas[i].bpp) {
1806  //TODO this is untested - didn't have any such subtitle stream
1807  cSubtitleClut *Clut = Page->GetClutById(sr->ClutId());
1808  dbgregions("reduce region %d bpp %d level %d area bpp %d<br>\n", sr->RegionId(), sr->RegionDepth(), sr->RegionLevelOfCompatibility(), Areas[i].bpp);
1809  bm->ReduceBpp(*Clut->GetPalette(sr->RegionDepth()));
1810  }
1811  else {
1812  dbgregions("condense region %d bpp %d level %d area bpp %d<br>\n", sr->RegionId(), sr->RegionDepth(), sr->RegionLevelOfCompatibility(), Areas[i].bpp);
1813  bm->ShrinkBpp(Areas[i].bpp);
1814  }
1815  }
1816  }
1817  bm->SetOffset(srr->RegionHorizontalAddress(), srr->RegionVerticalAddress());
1818  Bitmaps->AddBitmap(bm);
1819  }
1820  }
1821  }
1822  }
1823  if (DebugPages)
1824  Bitmaps->DbgDump(windowWidth, windowHeight);
1825 }
void SkipBit(void)
Definition: tools.h:396
const uint8_t * GetData(void) const
Definition: tools.h:401
int Index(void) const
Definition: tools.h:400
void WordAlign(void)
Definition: tools.c:1484
bool SetLength(int Length)
Definition: tools.c:1491
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:1469
void ByteAlign(void)
Definition: tools.c:1477
int GetBit(void)
Definition: tools.c:1460
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:1014
static void SleepMs(int TimeoutMs)
Creates a cCondWait object and uses it to sleep for TimeoutMs milliseconds, immediately giving up the...
Definition: thread.c:72
virtual void GetVideoSize(int &Width, int &Height, double &VideoAspect)
Returns the Width, Height and VideoAspect ratio of the currently displayed video material.
Definition: device.c:533
virtual int64_t GetSTC(void)
Gets the current System Time Counter, which can be used to synchronize audio, video and subtitles.
Definition: device.c:1279
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
cDvbSubtitleBitmaps(int State, int64_t Pts, int Timeout, tArea *Areas, int NumAreas, double OsdFactorX, double OsdFactorY, tArea &AreaCombined, tArea &AreaOsd)
Definition: dvbsubtitle.c:1270
void Draw(cOsd *Osd)
Definition: dvbsubtitle.c:1295
int64_t Pts(void)
Definition: dvbsubtitle.c:1262
bool HasBitmaps(void)
Definition: dvbsubtitle.c:1265
void DbgDump(int WindowWidth, int WindowHeight)
Definition: dvbsubtitle.c:1321
void AddBitmap(cBitmap *Bitmap)
Definition: dvbsubtitle.c:1290
cVector< cBitmap * > bitmaps
Definition: dvbsubtitle.c:1257
int Convert(const uchar *Data, int Length)
Definition: dvbsubtitle.c:1450
cList< cDvbSubtitleBitmaps > * bitmaps
Definition: dvbsubtitle.h:41
void FinishPage(cDvbSubtitlePage *Page)
Definition: dvbsubtitle.c:1767
virtual void Action(void)
A derived cThread class must implement the code it wants to execute as a separate thread in this func...
Definition: dvbsubtitle.c:1488
int ExtractPgsSegment(const uchar *Data, int Length, int64_t Pts)
Definition: dvbsubtitle.c:1702
int ExtractSegment(const uchar *Data, int Length, int64_t Pts)
Definition: dvbsubtitle.c:1582
cDvbSubtitleAssembler * dvbSubtitleAssembler
Definition: dvbsubtitle.h:26
cList< cDvbSubtitlePage > * pages
Definition: dvbsubtitle.h:40
static int setupLevel
Definition: dvbsubtitle.h:25
cDvbSubtitlePage * GetPageById(int PageId, bool New=false)
Definition: dvbsubtitle.c:1569
virtual ~cDvbSubtitleConverter()
Definition: dvbsubtitle.c:1373
static void SetupChanged(void)
Definition: dvbsubtitle.c:1382
int ConvertFragments(const uchar *Data, int Length)
Definition: dvbsubtitle.c:1404
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:2296
void Del(cListObject *Object, bool DeleteObject=true)
Definition: tools.c:2251
int Count(void) const
Definition: tools.h:640
void Add(cListObject *Object, cListObject *After=NULL)
Definition: tools.c:2219
int Index(void) const
Definition: tools.c:2139
Definition: tools.h:644
const cSubtitleObject * Next(const cSubtitleObject *Object) const
< Returns the element immediately before Object in this list, or NULL if Object is the first element ...
Definition: tools.h:663
const cSubtitleObject * First(void) const
Returns the first element in this list, or NULL if the list is empty.
Definition: tools.h:656
const T * Get(int Index) const
Returns the list element at the given Index, or NULL if no such element exists.
Definition: tools.h:653
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:296
int AntiAlias
Definition: config.h:336
int FontOsdSize
Definition: config.h:343
int SubtitleOffset
Definition: config.h:295
int SubtitleBgTransparency
Definition: config.h:296
char FontOsd[MAXFONTNAME]
Definition: config.h:337
Definition: tools.h:178
static cString sprintf(const char *fmt,...) __attribute__((format(printf
Definition: tools.c:1180
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:304
bool Running(void)
Returns false if a derived cThread object shall leave its Action() function.
Definition: thread.h:101
void Lock(void)
Definition: thread.h:94
void Cancel(int WaitSeconds=0)
Cancels the thread by first setting 'running' to false, so that the Action() loop can finish in an or...
Definition: thread.c:354
Definition: tools.h:404
void Set(int Ms=0)
Sets the timer.
Definition: tools.c:797
bool TimedOut(void) const
Definition: tools.c:802
int Size(void) const
Definition: tools.h:767
virtual void Append(T Data)
Definition: tools.h:787
cSetup Setup
Definition: config.c:372
#define PGS_PALETTE_SEGMENT
Definition: dvbsubtitle.c:28
#define DBGBITMAPWIDTH
Definition: dvbsubtitle.c:60
#define OBJECT_DATA_SEGMENT
Definition: dvbsubtitle.c:22
#define dbgpages(a...)
Definition: dvbsubtitle.c:50
#define DBGBACKGROUND
#define LimitTo32Bit(n)
Definition: dvbsubtitle.c:1486
#define PGS_OBJECT_SEGMENT
Definition: dvbsubtitle.c:29
static bool DebugNormal
Definition: dvbsubtitle.c:37
#define CLUT_DEFINITION_SEGMENT
Definition: dvbsubtitle.c:21
#define dbgsegments(a...)
Definition: dvbsubtitle.c:54
static bool DebugSegments
Definition: dvbsubtitle.c:44
#define END_OF_DISPLAY_SET_SEGMENT
Definition: dvbsubtitle.c:25
static bool DebugVerbose
Definition: dvbsubtitle.c:38
#define dbgpixel(a...)
Definition: dvbsubtitle.c:55
#define dbgcluts(a...)
Definition: dvbsubtitle.c:56
#define dbgregions(a...)
Definition: dvbsubtitle.c:51
#define REGION_COMPOSITION_SEGMENT
Definition: dvbsubtitle.c:20
#define DBGMAXBITMAPS
Definition: dvbsubtitle.c:59
static bool DebugCluts
Definition: dvbsubtitle.c:46
#define BORDER
#define dbgdisplay(a...)
Definition: dvbsubtitle.c:49
#define PAGE_COMPOSITION_SEGMENT
Definition: dvbsubtitle.c:19
#define STUFFING_SEGMENT
Definition: dvbsubtitle.c:26
#define PGS_WINDOW_SEGMENT
Definition: dvbsubtitle.c:31
#define dbgobjects(a...)
Definition: dvbsubtitle.c:52
static bool DebugRegions
Definition: dvbsubtitle.c:41
static bool DebugDisplay
Definition: dvbsubtitle.c:39
#define PGS_DISPLAY_SEGMENT
Definition: dvbsubtitle.c:32
static bool DebugConverter
Definition: dvbsubtitle.c:43
static bool DebugObjects
Definition: dvbsubtitle.c:42
static cSubtitleDebug SD
Definition: dvbsubtitle.c:139
#define DISPARITY_SIGNALING_SEGMENT
Definition: dvbsubtitle.c:24
#define dbgconverter(a...)
Definition: dvbsubtitle.c:53
#define PGS_PRESENTATION_SEGMENT
Definition: dvbsubtitle.c:30
static bool DebugPixel
Definition: dvbsubtitle.c:45
#define DISPLAY_DEFINITION_SEGMENT
Definition: dvbsubtitle.c:23
static bool DebugOutput
Definition: dvbsubtitle.c:47
static bool DebugPages
Definition: dvbsubtitle.c:40
#define dbgoutput(a...)
Definition: dvbsubtitle.c:57
uint32_t tColor
Definition: font.h: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:1351
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